Return the current Novell NetWare User ID


      FT_NWUID( [ <nConnection> ] ) -> cUid


     <nConnection> is a connection number, or logical station number,
     to find a userid for.  Under NetWare 286, this number can be from
     1 to 100.  Under NetWare 386, 1-250.  If not supplied, FT_NWUID()
     defaults to the current connection (i.e., the connection running
     the application).


     A string containing the userid, or "login name."
     The maximum length of this string, as defined by current
     versions of Novell NetWare, is 48 characters.


     FT_NWUID() returns the current NetWare userid, or "login
     name."  This is useful for implementing security or audit
     trail procedures within your programs.

     There is no simple way a user can "fool" this function into
     retrieving an incorrect value, provided a NetWare shell is loaded.

     This function requires FT_INT86() and FT_NWLSTAT()

     This function does NOT test for the existence of the NetWare shell.
     The behavior is undefined if no shell is loaded. You'll usually get
     garbage.  This function has not been tested on NetWare 386.


     QOut( "I am: " + FT_NWUID() )

     FOR x := 1 TO 100
       cUid := FT_NWUID( x )
       IF .NOT Empty( cUid )
         QOut( Str( x, 3 ) + Space(3) + cUid )

 Source: NWUID.PRG

 Author: Glenn Scott



 Wait on a NetWare semaphore (decrement)


      FT_NWSEMWAIT( <nHandle> [, nTimeout ] ) -> nRc


     <nHandle> is the semaphore handle, returned from a previous call
     to FT_NWSEMOPEN().

     <nTimeOut> is an optional parameter telling how long you wish to
     wait on this semaphore.  This is a numeric indicating the number
     of clock ticks (approx 1/18 sec ) to wait.  A zero (the default)
     means "don't wait."


     nRc, a numeric, as follows:

           0 - success
         254 - timeout failure
         255 - invalid semaphore handle


     See the description for the FT_NWSEMOPEN() function.


    FT_NWSEMOPEN( "Semaphore Test", nInitVal, @nHandle, @nOpenCnt )

    nRc := FT_NWSEMWAIT( nHandle )
    IF nRc == 254
       QOUT( "All slots for this resource are currently in use" )

 Source: NWSEM.PRG

 Author: Glenn Scott



 "Unlock" a semaphore locked by FT_NWSEMLOCK()


      FT_NWSEMUNLOCK( <nHandle> ) -> lRet


     <nHandle> is the semaphore handle returned from FT_NWSEMLOCK()


     lRet == .t. if you successfully unlock the semaphore, .f. if
     you don't.  If this call fails, it could be that you're passing
     an invalid semaphore handle.


     This call unlocks a semaphore prevsiously locked via FT_NWSEMLOCK().
     It is important that you get a valid semaphore handle from
     FT_NWSEMLOCK() before you use this call.  Make sure when you call
     FT_NWSEMLOCK() that you pass a numeric parameter in for the handle


     LOCAL nHandle := 0
     IF FT_NWSEMLOCK( "k:\apps\error.log", @nHandle )
         // Note, you aren't actually LOCKING this file, you are
         // just locking a semaphore by the same name.  As long as
         // all apps that might be using this file are cooperating
         // with the same kind of semaphore lock, you can effectively
         // control access to the file.
         QOUT("Couldn't lock file.")
       * Processing, then:
       FT_NWSEMUNLOCK( nHandle )

 Source: NWSEM.PRG

 Author: Glenn Scott



 Signal a NetWare semaphore (increment)


      FT_NWSEMSIG( nHandle ) -> nRc


     <nHandle> is the semaphore handle, returned from a previous call
     to FT_NWSEMOPEN().


     nRc, a numeric, as follows

          0 - success
          1 - semaphore overflow ( value > 127 )
        255 - invalid semaphore handle


      Use FT_NWSEMSIG() when your app has finished with the resource
      locked by a semaphore.  This will increase the value (thus
      making a slot available to another app).

      For more information, see the description under FT_NWSEMOPEN().


      QOUT( "Signal returns: " + STR( FT_NWSEMSIG( nHandle ) ) )

 Source: NWSEM.PRG

 Author: Glenn Scott




 Open or create a NetWare semaphore


       FT_NWSEMOPEN( <cName>, <nInitVal>, <@nHandle>, <@nOpenCnt> ) -> nRc


      <cName> is the semaphore name, maximum length is 127 characters.

      <nInitVal> is the initial value for the semaphore.  It must start
      as a positive number, to a maximum of 127.

      <@nHandle> is the semaphore handle.  THIS MUST BE PASSED BY
      REFERENCE!  On exit, <nHandle> will contain a numeric value that
      refers to the opened semaphore.  You will need it to pass to
      other semaphore functions!  PASS IT BY REFERENCE!

      <@nOpenCnt> is the number of stations that have opened the
      semaphore.  THIS MUST BE PASSED BY REFERENCE! On exit, <nOpenCnt>
      will contain a numeric value.


      nRc, a numeric result code, as follows:

            0 - success
          254 - Invalid semaphore name length
          255 - Invalid semaphore value

      <nHandle> will contain the semaphore handle, and
      <nOpenCnt> will contain the number of stations that have opened
      the semaphore.


      A semaphore is simply a label that indirectly controls network
      activity.  There is a semaphore name, which can be up to 127
      characters, and an associated value, which can range from 0 to

      A semaphore can be used for many things, but is most often used
      to limit the number of users in an application, and to control
      access to a network resource.

      A semaphore essentially allows you to place locks on resources
      other than files.

      An application begins the process by calling FT_NWSEMOPEN().
      If the semaphore doesn't exist, NetWare will create it.
      FT_NWSEMOPEN() returns a handle that is used in other semaphore

      Applications use FT_NWSEMWAIT() to wait for a semaphore to
      become available.  FT_NWSEMWAIT() decrements the semaphore's
      value by 1.  If the value > 0, then the application should
      be allowed to access the semaphore's resource.  If the value
      goes negative, then the application is placed in a queue.
      How long your app is in the queue is determined by how you
      set the timeout parameter.  If you can't get the resource in
      the time you allot, you're let out of the queue and the
      value increments by 1 again.

      When an application finishes with a semaphore, it should
      call FT_NWSEMSIG() to increment the value, and then
      FT_NWSEMCLOSE() to close the semaphore.  When the semaphore's
      open count goes to 0, NetWare deletes it.

      FT_NWSEMEX() can be used to examine the value and open count
      without affecting them.

      For an interesting discussion on the operating system aspects
      of semaphores, check "Operating Systems Design and Implementation"
      by A. Tanenbaum, page 60.  For more details on NetWare's
      semaphore facilities, refer to Charles Rose's "Programmer's
      Guide to NetWare".  The "Programmer's Guide" will make an
      excellent companion guide to the source code for all NetWare
      functions in the Nanforum Toolkit.


      LOCAL nInitVal, nRc, nHandle, nOpenCnt

      nInitVal := 2
      nRc      := FT_NWSEMOPEN( "Semaphore Test", nInitVal, ;
                                @nHandle, @nOpenCnt )

      IF nRc != 0
        QOUT =: "Error: " + STR( nRc ) )

 Source: NWSEM.PRG

 Author: Glenn Scott



 Perform a semaphore "lock"


      FT_NWSEMLOCK ( <cSemaphore>, <@nHandle> ) -> lRet


     <cSemaphore> is the name of a semaphore you want to "lock."
     <nHandle> is the semaphore's handle, if you get the lock.


     lRet == .t. if you get the lock, .f. if you don't.
     If the lock succeeds, <nHandle> will contain the semaphore
     handle.  If it fails, the value of <nHandle> is undefined.


     FT_NWSEMLOCK() uses the Nanforum Toolkit's NetWare Semaphore API
     functions in order to provide a general purpose "lock" you can use in
     a NetWare environment.

     An interesting byproduct of NetWare's semaphore functions is
     the "open count" which tells you how many connections have this
     semaphore open.  This is different from the semaphore's _value_,
     which is set when the semaphore is opened and changed with
     signal() and wait().

     The point of semaphores is that you don't care how many users
     are using the resource; you merely wait on a semaphore until
     the resource becomes available or you give up.  When you're done,
     you signal it and off you go.

     Back to the open count.  FT_NWSEMLOCK() opens the semaphore
     as named in <cSemaphore>.  After it is opened, the open count
     is checked.  If it is anything other than 1, that means someone
     else has it (or you failed in your open) so the semaphore is
     closed and the "lock" is refused.  If the value is 1, then your
     app is that 1 station so the "lock" is granted.

     You can use a semaphore lock to control access to anything
     that Clipper's RLOCK() and FLOCK() can't help you with, such
     as text files written with the low level file i/o functions,


     LOCAL nHandle := 0
     IF FT_NWSEMLOCK( "k:\apps\error.log", @nHandle )
         // Note, you aren't actually LOCKING this file, you are
         // just locking a semaphore by the same name.  As long as
         // all apps that might be using this file are cooperating
         // with the same kind of semaphore lock, you can effectively
         // control access to the file.
         QOUT("Couldn't lock file.")
       * Processing, then:
       FT_NWSEMUNLOCK( nHandle )

 Source: NWSEM.PRG

 Author: Glenn Scott



 Examine a NetWare semaphore's value and open count


       FT_NWSEMEX( <nHandle>, <@nValue>, <@nOpenCnt> ) -> nRc


      <nHandle> is the semaphore handle, returned from a previous call
      to FT_NWSEMOPEN().

      <@nValue> will get the current semaphore value.  THIS NUMERIC

      <@nOpenCnt> will get the current number of workstations
      that have opened the semaphore.  THIS NUMERIC ARGUMENT MUST BE


      nRc, a numeric, as follows:

            0 - success
          255 - invalid semaphore handle

      In addition, nValue will be set to the semaphore's current value,
      and nOpenCnt will be set to the number of stations that have
      opened the semaphore.


      See the description for FT_NWSEMOPEN().


    nInitVal := 2
    nHandle  := 0
    nOpenCnt := 0

    FT_NWSEMOPEN( "Semaphore Test", nInitVal, @nHandle, @nOpenCnt )

    nRc := FT_NWSEMWAIT( nHandle )
        IF nRc == 254
       QOUT( "All slots for this resource are currently in use" )

    FT_NWSEMEX( nHandle, @nValue, @nOpenCnt )
    QOUT( "Semaphore test -> Open at [" + ;
          ALLTRIM(STR(nOpenCnt))        + ;
          "] stations, value is ["      + ;
          ALLTRIM(STR(nValue)) + "]" )

 Source: NWSEM.PRG

 Author: Glenn Scott




 Close a NetWare semaphore


      FT_NWSEMCLOSE( <nHandle> )  -> nRc


     <nHandle> is the semaphore handle, returned from a previous call
     to FT_NWSEMOPEN().


     nRc, a numeric, as follows:

            0 - success
          255 - invalid semaphore handle


     Call FT_NWSEMCLOSE() when the app is finished.  This decrements
     the open count for the semaphore.  If the open count hits zero,
     the semaphore is deleted by NetWare.


     QOUT( "Close returns:  " + STR( FT_NWSEMCLOSE( nHandle ) ) )

 Source: NWSEM.PRG

 Author: Glenn Scott


C5DG-7 DBFNTX Driver

Clipper 5.x – Drivers Guide

Chapter 7

DBFNTX Driver Installation and Usage

DBFNTX is the default RDD for Clipper. This new database driver replaces the DBFNTX database driver supplied with earlier versions of Clipper and adds a number of new indexing features. With DBFNTX, you can:

. Create conditional indexes by specifying a FOR condition

. Create indexes using a record scope or WHILE condition, allowing you to INDEX based on the order of another index

. Create both ascending and descending order indexes

. Specify an expression that is evaluated periodically during indexing in order to display an index progress indicator

In This Chapter 

This chapter explains how to install DBFNTX and how to use it in your applications. The following major topics are discussed:

. Overview of the DBFNTX RDD

. New Locking Scheme

. Conditional Indexing

. Installing DBFNTX Driver Files

. Linking the DBFNTX Driver

. Using the DBFNTX Driver

. Compatibility with dBASE III

Overview of the DBFNTX RDD

As an update of the default database driver, DBFNTX is linked into and used automatically by your application unless you compile using the /R option.

New Features

The replaceable driver lets you create and maintain (.ntx) files using features above and beyond those supplied with the previous DBFNTX driver. The new indexing features are supplied in the form of several syntactical additions to the INDEX and REINDEX commands. Specifically you can:

. Specify full record scoping and conditional filtering using the standard ALL, FOR, WHILE, NEXT, REST, and RECORD clauses

. Create an index while another controlling index is still active

. Monitor indexing as each record (or a specified record number interval) is processed using the EVAL and EVERY clauses

. Eliminate separate coding for descending order keys using the DESCENDING clause


Index files (.ntx) created with the original DBFNTX driver are compatible with DBFNTX and can be used in new applications without reindexing. Index files (.ntx) created with this version of DBFNTX will also work with previous Clipper applications provided that you use no FOR, WHILE, <scope>, or DESCENDING clauses.

Important! Indexes produced with DBFNTX using FOR or DESCENDING are incompatible with earlier version (.ntx) files. If you attempt to access them with the original DBFNTX database driver or programs compiled with versions earlier than Clipper 5.2, you will get an unrecoverable runtime error. In Clipper, this generates an “index corrupted” error message, causing the application to terminate.

New Locking Scheme

The DBFNTX database driver implements a new locking scheme to resolve several problems identified in previous versions of Clipper and to prevent potential problems that might arise when running Clipper applications in a network environment. This section discusses these changes and their implications, including compatibility issues.

Lock Time-outs

Problem: Index locking in previous versions of Clipper was handled automatically by the database driver, and had no time-out provision. This created the potential for problems in network environments if a workstation died while holding a lock. If this situation occurred all other workstations waiting for an index lock would appear to freeze while waiting to obtain their lock. This could also happen if a user placed a Clipper application in the background on a multitasking system without sufficient processing time allocated to it. Eventually, most network operation systems would clear a connection that had no activity for a specified period of time. This would free the lock and everything would resume as normal, but frustrated users may have rebooted their machines possibly causing file corruption.

Solution: In Clipper 5.2 the NTX driver will generate a recoverable runtime error if it fails to lock the index after a predetermined number of retries. The default error handler for this system simply returns (.T.) to retry the operation. This emulates the behavior of previous Clipper versions.

Error Handling

Time out handling: The handling of this error is problematic because the lock is issued from various internal index routines. Therefore the only safe recoveries are to retry or quit. Choosing to default from the error or issuing a break will more than likely leave the index in a corrupted state. If either of the options is employed, the application should immediately recreate the index. The preferred way to handle a time out such as this is to alert the user of the situation so they don’t think their machine has hung, and then have the network administrator determine what workstation is causing the problem. When the problem workstation is cleared, the users that have timed out can select retry and continue processing.

NTXERR.PRG: The file NTXERR.PRG contains the source code for the default error handler INIT procedure. This error handler can be modified to allow user-defined error handling for index lock time-outs. Care should be exercised when modifying the error handler as detailed above.

Compatibility: The lock time-out capability when used in conjunction with the default error handler is totally compatibility with previous versions of Clipper. No changes are made to the NTX file structure and no action is required by the developer to activate the time-out functionality.

New Lock Offset

Problem: Index locking, which is transparent to the developer, uses a single-byte semaphore locking system. This semaphore was placed at a virtual offset (beyond the physical end of file) in the index file. In previous versions of Clipper, this offset was located at one billion (1,000,000,000) which was adequate at the time. But many systems today are capable of producing indexes that are large enough to cause the actual data present at the lock offset to become physically locked. This leads to problems when trying to read or write to the data at that offset.

Solution: The solution is to move the offset where locking occurs to a location at a greater offset. We have chosen FFFFFFFF hex, which is the largest offset possible under the DOS operating system. The problem with this solution is that new applications using the index will be locking this new byte while old applications using the same index will lock the old position. Clearly this would cause both applications to fail because each could have a lock on the file at the same time.

To avoid this, the signature of the index (in the index header) is modified to prevent pre-Clipper 5.2 applications from being able to open the index. Clipper 5.2 applications can detect the correct offset to use by the flag in the header and will automatically use the correct one. In Figure 7-1 below, each bit represents a flag:

BIT  7 6 5 4 3 2 1 0
R Reserved
I Index type - both bits set (NTX)
C Index created with a Condition, condition in header
T Created as a Temporary index
O New Offset for exclusive (semaphore) lock
Figure 7-1: Bit Field for the Signature Byte of a -Clipper 5.2 NTX File


If Clipper 5.2 automatically modified the signature in the header when it created indexes, programs with automatic reindexing routines would be creating indexes that appeared corrupt to pre-Clipper 5.2 applications. This has an obvious problem with backward compatibility. Therefore, in order to create indexes with the new signature, the developer must link in the module NTXLOCK2.OBJ with the full knowledge that this will create indexes that older applications will not be able to access.

Header Changes

The signature byte of a .NTX file is 6 for an unenhanced NTX index. The inclusion of the NTXLOCK2.OBJ will cause the signature to become 26 hex. (6 hex ORed with 20 hex). See Figure 7-1 for an illustration of all the possible values for the signature byte.

Error Handling

Clipper 5.2 applications will automatically recognize the signature byte of the header, and depending on the signature value, will use the correct index lock location. Applications built with previous versions of Clipper, however, do not have the capability to detect the optional new information in the signature byte. Therefore, when an order application tries to open a file that has been created with the NTXLOCK2.OBJ linked in it will produce a Corruption Detected error.


The new locking location, if used, is not backward compatible with applications compiled with previous versions of Clipper.

Indexes created by applications built with a previous version of Clipper can be used by Clipper 5.2 using the new location and will not be modified unless the index is recreated in application.

Since older applications have no knowledge of the new index locking scheme nor of the significance of the header signature, these applications will assume the index is corrupt and will produce an Index Corrupted error.

Conditional Indexing

Conditional indexes are a feature of the DBFNTX driver. This section discusses this feature of the DBFNTX driver in some detail, giving you specific information about the implementation of conditional indexes. Compatibility issues are also discussed.

Conditional Indexes

Conditional indexes are produced by using a FOR condition in the index creation process. These indexes are made fully maintainable by storing the FOR condition in the index header. This condition is subsequently retrieved and compiled each time the index in opened. During updates, items are added to the index only if they meet the criteria of the condition.

Since older applications do not have the ability to recognize and use the condition stored in the header, they must be prevented from opening the index since they corrupt the index. This is accomplished by modifying the signature of the index (in the index header) preventing pre-Clipper 5.2 applications from being able to open the index. Clipper 5.2 applications can detect the flag in the header and will automatically use the stored FOR condition correctly.

Temporary Indexes

Temporary indexes are produced by using any scoping clause other than the FOR condition in the index creation process. These indexes are not automatically maintainable because the condition is not stored for later use. These indexes can be made maintainable if the condition can be expressed as a FOR condition and is added using the FOR clause. But the main use of temporary indexes is for fast creation of indexes for read- only browses or reports that operate on a subset of the database.

Since older applications would not operate properly with indexes that do not contain all the keys in a given database, they must be prevented from using them. This is accomplished by modifying the index signature to prevent pre-Clipper 5.2 applications from being able to open the index.


Conditional Indexes

The developer need only specify the FOR condition when creating the index. In doing so he must be fully aware the index will no longer be accessible to pre–Clipper 5.2 applications.

Temporary Indexes

The developer need only specify a scope other than FOR when creating the index. In doing so he must be fully aware the index will no longer be accessible to pre-Clipper 5.2 applications and that the index created is not maintainable.

Header Changes

The signature byte of a .NTX file is 6 for a unenhanced NTX index. If the index is created as a conditional index it will have a signature of 7 hex (6 hex ORed with 1 hex). If the index is created as a temporary index it will have a signature of E hex. (6 hex ORed with 8 hex). See Figure 7-1 for an illustration of all the possible values for the signature byte.

Error Handling

Corruption Detected

Since older applications have no knowledge of the new index features nor how to interpret the additional flags in the header signature, these applications will assume the index is corrupt and will produce an Index Corrupted error.


If an index is created with a FOR condition and an attempt is made to update the index with a key that does not match the condition, the update is suppressed and the index is placed at EOF(). This is consistent with the current behavior for indexes created with the unique flag when an update is attempted with a non-unique key.

Also if a navigational action is attempted (SKIP) and the current record is not found in the index, the index will place the record pointer at EOF(). This is true for both conditional and temporary indexes.


Backward Compatibility

If the conditional or temporary indexing features are used the index produced will not be backward compatible with applications compiled with previous versions of Clipper. Indexes that do not use the features, however, will be 100% compatible.

Forward Compatibility

Indexes created by applications built with a previous version of Clipper can be used by Clipper 5.2 and will not be modified unless the index is recreated using either the conditional or temporary index features.

Error Message Produced by Old Applications

Since older applications have no knowledge of the new index locking scheme nor of the significance of the header signature, these applications will assume the index is corrupt and will produce an Index Corrupted error.

Installing DBFNTX Driver Files

DBFNTX is supplied as the file DBFNTX.LIB.

The Clipper installation program installs this driver as the default in the \CLIPPER5\LIB subdirectory on the drive that you specify, so you need not install the driver manually.

Important! Before installing Clipper, you may want to rename the DBFNTX.LIB that currently resides in your \CLIPPER5\LIB directory to DBFNTX.001. The new version, when installed, will overwrite DBFNTX.LIB. If you do not rename or otherwise protect the old version of DBFNTX.LIB, you will lose it.

Linking the DBFNTX Database Driver

Since DBFNTX is the default database driver for Clipper, there are no special instructions for linking. Unless you specify the /R option when you compile, the new driver will be linked into each program automatically if you specify a USE command or DBUSEAREA() function without an explicit request for another database driver. The driver is also linked if you specify an INDEX or REINDEX command with any of the new features.

Using the DBFNTX Database Driver

In applications written for the new DBFNTX driver, you can use the INDEX and REINDEX commands exactly as you have used them in the past. The index files (.ntx) you create and maintain in this way are completely compatible with those created using previous versions of the driver.

Changes to existing code are necessary only if you use the new indexing features. The (.ntx) files you create using the new features will have a slightly different header file and cannot be used by programs linked with a previous version of the driver.

Using (.ntx) and (.ndx) Files Concurrently

You can use (.ntx) and (.ndx) files concurrently in a Clipper program like this:

// (.ntx) file using default DBFNTX driver


// (.ndx) files using DBFNDX driver


Note, however, that you cannot use (.ntx) and (.ndx) files in the same work area. For example, the following does not work:

USE File1 VIA "DBFNDX" INDEX File1.ntx, File2.ndx

Compatibility with dBASE III PLUS

The default DBFNTX driver makes Clipper programs behave differently than traditional dBASE programs. Some of these differences are discussed below.

Supported Data Types

The DBFNTX database driver supports the following dBASE III PLUS- compatible data types for key expressions:

. Character

. Numeric

. Date

. Logical

Supported Key Expressions

When you create (.ntx) files using the DBFNTX driver, you can use all Clipper or user-defined functions compatible with dBASE III PLUS as well as other functions accepted by the extended Clipper functionality.

Error Handling

The indexing behavior of DBFNTX and DBFNDX in a Clipper application is identical unless otherwise noted. With the default DBFNTX driver, you can handle most errors using BEGIN SEQUENCE…END SEQUENCE as illustrated in the next section.


In Clipper, you can use the FIND command only to locate keys in indexes where the index key expression is character data type. This differs from dBASE III PLUS where FIND supports character and numeric key values.

Note: In Clipper programs, always use the SEEK command or the DBSEEK() function to search an index for a key value.

The DBFNTX driver lets you recover from data type errors raised during a FIND or SEEK. However, since Error:canDefault, Error:canRetry or Error:canSubstitute are set to false (.F.), you should use BEGIN SEQUENCE…END to handle such SEEK or FIND data type errors. Within the error block for the current operation, issue a BREAK() using the error object that the DBFNTX database driver generates, like this:

bOld := ERRORBLOCK({|oError| BREAK(oError)})
     SEEK xVar
     // Recovery code END

There is an extensive discussion of the effective use of the Clipper error system in the Error Handling Strategies chapter of the Programming and Utilities guide.

Sharing Data on a Network

The DBFNTX driver provides file and record locking schemes that are different from dBASE III PLUS schemes. This means that if the same database and index files are open in Clipper and in dBASE III PLUS, Clipper program locks are not visible to dBASE III PLUS and vice versa.

Warning! Database integrity is not guaranteed and index corruption will occur if Clipper and dBASE III PLUS programs attempt to write to a database or index file at the same time. Therefore, concurrent use of the same database (.dbf) and index (.ndx) files by dBASE III PLUS and Clipper programs is strongly discouraged and not supported by Computer Associates.


In this chapter, you were given an overview of the new features of the default DBFNTX RDD. You learned how to this driver is automatically linked and how to use it in your applications, and were given an overview of the compatiblity issues.

C5 Error Management

Clipper Error Management

You may encounter several categories of error messages when you develop applications in Clipper. Run-time errors occur when your application hits a snag, and Clipper gives you other messages when you compile and link the application.

You will discover that the greatest number of messages are returned to you by the Clipper Compiler. You don’t risk missing error messages by using a third-party linker.

Clipper’s Run-Time Errors

One product enhancement in the Summer ’87 release of Clipper is a total revamping of error handling. In the past, most error messages would prompt the operator to ‘Quit, Abort or Ignore’. The Summer ’87 release no longer routinely prompts the operator but simply

– present an error message, wich cities the
– procedure name,
– line number, and a simple explanation of the error;

– closes all files; and

– return to DOS.

There are two types of errors in Clipper applications;

– recoverable and

– non-recoverable.

‘Recoverable Errors’ are ones that you can trap and deal with in your applications. You may not always be able to fix the problem, but you can avoid aborting to DOS. This category represents the bulk of errors you will encounter.

‘Non-recoverable’ errors can be introduced by C or assembly routines, corrupted data files, hardware errors and so on. There is no way to handle nonrecoverable errors inside the application.

There are six basic error groups. Nearky all of them have to do with ‘source code errors’ and they occur because you may have tried to add a character string to a number, or you tried to print a variable that doesn’t exist, and so on. You will find 96 percent of potential errors that happen when operators type things that make the program “hiccup”. These errors are also programmer’s responsibility. In some way, the programmer didn’t validate the user input well enough to keep a request for nonexistent files from being processed, or the error occurs for some other similarly pedestrian reason.

Whenever you realize that some errant variable might cause a problem, you have half of the problem solved. Fully half of the battle is seeing what can go wrong – and why.

The other half of the battle is developing an error trap that will catch the bugs before they trigger a nonrecoverable error. What do you do when a user tries to duplicate account number ? What do you do when a record
locks fails ?

Trapping Errors with the

Clipper gives you some interesting tools to trap error conditions before they can damage your application. For example, the Summer ’87 release include a new command construct, the BEGIN SEQUENCE command. BEGIN SEQUENCE has the following syntax:

   program code ...
   program code ...
   program code ... && Usually error recovery procedures.

The BEGIN SEQUENCE command  is similar to a DO WHILE loop in the contruction of its code. Unlike the DO WHILE loop, however, BEGIN SEQUENCE is meant to make an umbrella over a group of procedures. Any time the BREAK command is encountered, control of the program immediately passes to the first line after the END statement. Some Clipper programmers use BEGIN SEQUENCE as a sneaky way of emulating the unsupported RETURM TO MASTER command, but its actual intent is to enable you to program your own disaster recovery routines.

The BEGIN SEQUENCE command is most useful in keeping an error condition from exiting the program and displaying the DOS prompt. See the BEGIN SEQUENCE command listing in Part III for more information.

Recoverable Error Functions

The nonfatal or recoverable errors are divided into six basic groups. CLIPPER.LIB include six error-handling function – one for each of the six basic error groups. The error handling functions are written in Clipper code that you can alter to customize your application’s error handling. These are exracted for you on the Clipper disks in a file called ERRORSYS.PRG. The six group are:

Database errors
Expression errors
Miscellaneous errors
Open errors
Print errors
Undefined errors

Note: The file ALTERROR.PRG on your Clipper disks contains a set of error functions specifically designed for use with the Clipper debugger.

Producing Your Own Recovery Functions

You can modify the default error functions by replacing them with user-defined functions that have the same name. Group these replacement functions into a file and compile them. When it comes time to link your program together, add that .obj file to the FILES directive to the linker. Do not specify the error object file as the first name in the list. Always put your application’s main module first in the list. As long as your functions have the same name as the ones in CLIPPER.LIB, they will be linked into the program rather than the functions in the library.

Each of the error-handling functions that are supplied with the Clipper library share the same basic syntax:

XXX_ERROR(name, line, info [,model][,_1][,_2][,_3][,_4][,_5]

The XXX in the FUNCTION name is filled with the name of actual function itself.

The ‘name‘ parameter returns a character string equal to the name of the procedure or function that was active when the error was triggered.

The ‘line‘ argument refers to the program line number assigned by the Clipper compiler. If you compiled the application with the -l switch for no line number, this parameter will be zero.

The ‘info‘ (information) parameter contains a character string that describes the error-for example, DATA TYPE MISMATCH or DIVISION BY ZERO.

The ‘model‘ parameter is optional and is not supplied by some of functions When it is supplied, the model argument to the function is the portion of source code that triggered the error.

The _1 ,_2 ,_3 ,_4 ,_5 parameters – if they are supplied by the function – can be of any type; character, numeris, logical, or date. You can look at these as being the parameters that were passed to the command when the error was triggered, and they will contain values that were in effect at the time the error was triggered. The unusual names reflect a desire on the part of the Clipper development team not to conflict with any variable names to be reserved words. Also because Clipper is written in Microsoft C 5.0, you should aviod using using underscore (_) as the first character in a field, file or variable name. This could bugs that seem to make no sense by invoking internal Clipper functions.

By identifying and manupulating the errors and the information associated with an error, you can develop a strategy to recover from that error. To develop recovery procedure, look at each of the error functions individually. In general, the value that is returned by the error function determines what action the Clipper program takes.

The code included in the next six examples is reprinted by permission of Nantucket Corporation and is the actual code found in CLIPPER.LIB. It is also included on your factory disks in a file called ERROR.DOC.

Handling Database Errors

Database errors are classified as those errors that occur during the time
a file is open. This does not include the opening and closing operations.
REPLACE, DELETE, AVERAGE, and similar commands are the types of commands
that can trigger this error message.

The following syntax is for the error-hanfling function DB_ERROR():

* db_error( name,  line,  info)
FUNCTION db_error
   PARAM name, line, info
   @ 0,0
   @ 0,0 SAY 'Pro- c ' + M->name +;
             ' line ' + LTRIM(STR(M->line)) +;
             ', ' + M->info

After declaring the function name, ‘DB_ERROR()’ recognizes three parameters: ‘name’, ‘line’, and ‘info’.

The function then SETs the DEVICE to the SCREEN so that the error message is displayed even if the application is printing at the time.

The command NOTE BREAK suggests that you might prefer to issue a BREAK to your BEGIN SEQUENCE construct rather that the QUIT.

The ‘info‘ parameter may return one of these messages:


This message occurs when you attempt to access a data file in a work area where none is open.


This error message is triggered when your application attempts to perform a command that requires file or record locking. When you have used a data file in a nonexclusive mode, you must continue to follow networking rules throughout the entire application.


This error message is given whenever your application execute a command that requires exclusive use of a file.


This message indicates an application attempted to write into a field a number that is larger than the length of the field.


This message occurs when an application attempts to manipulate an index file that is damaged.

You can modify DB_ERROR() to perform actions that will try to rectify the situation. Use the value of the variable, ‘info’ to key a DO CASE structure to specify corrective action. For example:


you could have the function rebuild the index.

You have three choices of action when the DB_ERROR() function is triggered:

– You can perform actions to correct the situation and RETURN the value of the function as true (.T.) RETURNing a true (.T.) retries the command that triggered the error.

– You can return false (.F.) and abort the program to DOS.

– You can issue a BREAK statement as part of a BEGIN SEQUENCE contruct. The control of the program would revert to the next line following the END command. Your program then can back out of the situation as gracefully as possible, repaint the main menu, and start fresh.

Handling Expression Errors

Expression errors generally are associated with conditions such as division by zero or data type mismatches. A good example of this would be a command in your ptogram that tries to add a number to a character string. This causes a data type mismatch error to occur.

This error occurs whenever a Clipper application contains an invalid expression. For example, in VAL(‘AU-H20’) the value of function has no mathematical meaning.

The following syntax is for the error-handling function EXPR_ERROR();

* expr_error(name, line, info, model, _1, _2, _3 )
FUNCTION expr_error
   PARAM name, line, info, model, _1, _2, _3

   @ 0,0
   @ 0,0 SAY 'Proc ' + M->name + ' line ' +;
             LTRIM(STR(M->line)) + ', ' + M->info

The ‘model‘ portion of the function argument contains the expression that triggered the error. In all cases, that expression is delivered as a character string. The operand parameters have values equal to the variables in effect when the error was triggered. In other words, the model is returned as a piece of code with the operands substitued as variable elements of the code. For example:

TONE( c#,277 )

would return the following values in EXPR_ERROR():

info = 'Expression Error'
model = 'TONE(_1,_2)'
_1 = 'c#'
_2 = 277

If you devise a lookup function so that your EXPR_ERROR function can “know” what the TONE() function should look like, it is fairly simple matter to compare data types of the operands to find out that _1 should be numeric. The function then could substitute a numeric value into _1 and RETURN &model. This would reevaluate the expression, and control would be passed to the next line in the program if the error condition was corrected.

The ‘information’ parameter of the EXPR_ERROR() function returns one of the following messages:


This error occurs when two different types of variable are in some way joined or operated upon jointly.


This error occurs if your Clipper application attempts to use an array element that does not exist.


This error occurs whenever your Clipper application attempts to divide any number by 0, which is mathematically meaningless and therefore illegal.

Handling Miscellaneous Errors

Miscellaneous errors consist of lower-level errors that usually would not be considered fatal. This function also acts as a “dust bin,” handling those errors that the other error functions are not equipped to handle.

The following syntax is for the error-handling function MISC_ERROR():

* misc_error(name, line, info, model)
FUNCTION misc_error
   PARAM name, line, info, model
   @ 0,0
   @ 0,0 SAY 'Proc ' + M->name + ' line ' + ;
              LTRIM(STR(M->line)) + ', ' + M->info

You cannot use the MISC_ERROR() function to return the model argument as executable code, as you can with the EXPR_ERROR() function.

To recover from a MISC_ERROR(), you can try retry the operation by returning true (.T.) after making some changes to variables. You can issue the BREAK command to jettison out of a BEGIN SEQUENCE construct, or you can abort to DOS.

The ‘info’ parameter may return one of the following messages:


The TYPE MISMATCH error message occurs when a REPLACE command attempts to place the wrong data type into a field. For example, trying to place “123” into a numeric field would cause a type-mismatch, miscellaneous error.

Note: This is different from the TYPE MISMATCH in the expression error-handling function. The context of the error determines which function is called. If there is not a write to the file, the expression error will be triggered. If it involves writing to a file, the miscellaneous error will be called.


The RUN ERROR error message occurs when a Clipper application attempts to run another executable file by loading a new COMMAND.COM into memory. The underlying cause of this error can be determined by reading the value of DOSERROR() function. The ‘model’ parameter contains the portion of your Clipper code that triggered the error.

Handling Open Errors

Open errors occur when file-opening operations fail. Special consideration is given to opening data files. When the request to open a data file is not honored by DOS (usually by passing along a network operating system message), an evaluation is made. If NETERR() is true and the ‘model’ paramater is USE, the error function simply returns a value of false. In such a case, the application ignores the error.

This is why you have to structure the USE command the way you do in a Clipper application. If your application tries to open a data file and fails, the error is ignored, and the next portion of code is executed. If the next portion of code tries to open index files for a data file that could not be opened, a second, more serious, error occurs. The open error on the index files is not ignored, and the application aborts to DOS.

The following syntax is for the error-handling function OPEN_ERROR() :

 * open_error(name, line, info, model, _1)
 FUNCTION open_error
    PARAM name, line, info, model, _1
    IF NETERR() .AND. model == 'USE'
       RETURN .F.
    @ 0,0
    @ 0,0 SAY 'Proc ' + M->name + ' line ' + ;
              LTRIM(STR(M->line)) + ', ' + M->info + ' ' +;
              M->_1 +' (' + LTRIM(STR(DOSERROR())) + ')'
              @ 0,65 SAY 'Retry ? (Y/N)'
    IF .NOT. CHR( LASTKEY() ) $ 'Yy'
    @ 0,0

OPEN_ERROR() is the most active of the six types of error-handling functions. First, it terminates itself in the case of USE errors where NETERR() is true. Thise helps you determine what you need to do about unavailable files. (Refer to Chapter 7, “Clipper and Networking” for more information.) Then, OPEN_ERROR() prompts you to retry the operation.

The ‘info’ parameter of the OPEN_ERROR() returns only the message OPEN ERROR. The ‘model’ argument to the function is the command that triggered the error. The operand parameter, _1, is the file name that triggered the error.

To recover from an OPEN_ERROR(), you can return true (.T.) and retry the operation. Returning false ignores the command that created the error. Further error messages are certain if the application tries to perform operations on a file that is not open. You could also issue a BREAK, which would provide you with your own error-recovery facility if you have a BEGIN SEQUENCE construct in your application.

Handling Print Errors

A print error occurs whenever your Clipper application is running DOS 3.1 or higher and is accessible a parallel port that is sensed to be not ready. Users running earlier versions of DOS get the straight DOS ‘Abort, Retry, Ignore?’ error message without being able to trap the error first. Usually, the operator has forgotten to place the printer on-line. This error occurs also when a network spooler is unable to continue receiving output or when you redirect @ … SAY commands to a file with the SET PRINTER TO command, and a disk error occurs.

If the printer has been redirected with MODE to a serial port, an OPEN ERROR error is triggered instead.

The following syntax is for the error-handling function PRINT_ERROR():

* print_error(name, line)
FUNCTION print_error
   PARAM name, line
   @ 0,0
   @ 0,0 SAY 'Proc ' + M->name + ' line ' + ;
              LTRIM(STR(M->line)) + ', printer not ready'
   @ 0,65 SAY 'Retry ? (Y/N)'
   IF .NOT. CHR( LASTKEY() ) $ 'Yy'
   @ 0,0

The ‘info’ parameter always returns the string PRINTER ERROR.

You can recover from the PRINT_ERROR function by prompting the operator to correct the situation, then returning true (.T.) which will retry the command.

BREAK can be used to terminate the error function and pass control to a custom error-handling portion of your Clipper application.

Returning false (.F.) continues the application without retrying the command that triggered the error message. This can lead to problems because the next command may also access the printer; your program’s operator will see an endless srtring of error messages.

Because the error message automatically SETs DEVICE TO SCREEN, a retry would cause any @ … SAY commands to be displayed on the screen, causing another error. If you are doing to develop your own PRINT_ERROR() function, use a semaphore variable that will store the condition of the SET DEVICE command. In that way, your error-recovery code will “know” if it must SET DEVICE TO PRINT in order to resume without further errors.

Handling Undefined Errors

The UNDEF_ERROR() function is triggered whenever you refer to a variable that has not been defined. You can consider this to be any situation in which the TYPE() function would return a value of ‘U’.

The following syntax is for the error-handling function UNDEF_ERROR():

* undef_error(name, line, info, model, _1)
FUNCTION undef_error
   PARAM name, line, info, model, _1
   @ 0,0
   @ 0,0 SAY 'Proc ' + M->name + ' line ' + ;
              LTRIM(STR(M->line)) + ', ' + M->info + ' ' + M->_1

The ‘info’ parameter returns one of the following character strings:


This message is given whenever you attempt to access a memory variable name that does not exits.


This error message indicates your Clipper application accessed an array element that does not exist, or the array itself does not exists.


This error message occurs whenever your application refers to a user-defined function or procedure that has not been declared within the application itself. This would apply also to any function contained in the the Extend library that has not been referenced within the body of the application. This error message usually indicates that a function has been used in either a label or report form but has not been invoked in the application itself. Whenever you use a function in a report form that is not used is the application itself, use the EXTERNAL command so that the compiler includes the function in the .EXE file.

The ‘model’ parameter contains the portion of the source code that triggered the error. The _1 operand contains the name of the unidentified identifier.

In recovering from this error, returning true (.T.) retries the operation. The BREAK command could be used to exit out of a BEGIN SEQUENCE construct to a custom error-handling routine. Returning false (.F.) causes Clipper to close all files and return to DOS.
Nonrecoverable Errors

Yor Clipper application may trigger other errors that are not handled by the previously described error-handling functions. These errors include the following:


An INTERNAL ERROR message generally is caused by a corrupted index file. Clipper presentsthe error message and pauses for a key to be pressed. Pressing any key causes Clipper to quit and return to DOS.


This error occurs when the application is writing a database file back to disk, and the write attempts to go beyond the amount of available disk space. This error message displays a prompt, allowing the user to retry the operation, just in case the cause of the error is a floppy disk that is not ready. Answering ‘No’ to the prompt causes the application to quit and return you to DOS. In essence, there is little real choice here because the pause does not include the opportunity to delete any files.


This is triggered when the error that begins the process is so bad that it cause an error within one of the error functions. Clipper indicates this by posting this error message and prompting you to press any key. When you press any key, the application quits and return to DOS.


This error displays when the upper limits of RAM have been reached. Clipper display the error message and pauses; pressing any key causes Clipper to abort and return you to the DOS prompt.


This error happens whenever Clipper senses that the application does not have enough memory to perform an operation. Clipper displays the message and pauses. Pressing any key causes Clipper to quit, close all files, and return you to the DOS prompt.

Error Messages Returned at
Compile and Link Time

During the development cycle, you will get error messages telling you that there are errors in your program. Ninety percent of these errors come from the Clipper compiler. The other 10 percent are presented by the linker.

Compiler Error Messages

The compiler itself is your front-line debugging tools. Most syntactical bugs and errors in your program are caught by the compiler and noted for you.

You can redirect or echo the Clipper error messages to a printer or a file. To redirect Clipper error messages to the printer, simply enter your commands as you normally would, adding a standard argument for DOS output redirection. For example :

CLIPPER prognam > PRN

This sends any output from Clipper to the printer. There are a great number of errors that you might see as you use the compiler. Most are self-explanatory. They tell you the procedure name, the line number of the program, and show you the piece of code that is causing the problem. Clipper also points to problem with caret (^). Figure 8.1 shows a typical Clipper error message.

 Fig. 8.1 Clipper error message.
 C:\iii\clip\book>clipper dbedit
 The Clipper Compiler, Summer '87
 Copyright (c) Nantucket Corp 1985-1987, All Rights Reserved,
 Microsoft C Runtime Library Routines,
 Copyright (c) Microsoft Corp 1985-1987, All Rights Reserved,
 Compiling DBEDIT.PRG
 line 9: rest of line ignored
 CLEAR ALL the extra stuff off the screen
 1 error
 Code Pass 1
 Code Pass 2
 Code size 443, Symbols 160, Constants 181

Documenting all of the errors that the compiler can return to you would be nearly impossible. Almost any command with an incorrect syntax will produce the same type of error message that dBASE III Plus delivers in its interpretive mode except that Clipper does not ask if you want to cancel, suspend, or ignore. Other error messages, however are fairly common and require only small amounts of clarification. The following is an alphabetical list of errors returned by the compiler.


This error message suggest that you have placed a CASE comparison in your program but forgot to begin with a DO CASE statement.


You have probably placed an ELSE statement in the wrong place, The compiler does not find a matching IF for ELSE.


This error normally occurs when you remove a DO CASE statement from a program but forget to erase the ENDCASE at the bottom. Clipper is telling you that it has found an ENDCASE statement but no DO CASE statement to open the construct.


The compiler has found an ENDDO that is unbalanced. There is no corresponding DO WHILE to open the construct.


This error is most likely to occur when you are nesting IF … ELSE … ENDIF statements or have erased an IF construct but have forgotten to remove the ENDIF.


Clipper has found an EXIT command, requesting Clipper to drop out of a DO WHILE loop, but Clipper does not find a DO WHILE construct.


This error is triggered if two programs contain the same procedure name or if you have created two user-defined functions with the same name. This message is displayed only to the screen and is not affected by DOS redirection.


You have a syntax error in a SET DEVICE TO command.


This is triggered whenever the left side of an expression using the equal sign (=) is an illegal expression, or the STORE command tries to perform an illegal variable initialization.

– for example, STORE 8 TO 5+5.


Clipper has located a LOOP command that is not inside a DO WHILE construct.


This error is triggered by omitting the closing quotation marks on string expressions.


This error message means that a NEXT command has no balancing FOR command.


Clipper has found an OTHERWISE command that is not inside a DO CASE construct.


This error message is displayed usually when you have given an illegal argument or option to a command. The illegal portion is to the right of the command verb, so it is looked at as unnecessary baggage. Clipper simply ignores the existence of anything else on that line.


This error message is triggered usually when a procedure name has been duplicated in an application.


The compiler is limited to a 64K constant table to keep track of elements of the program while it is being compiled. If you get this message, break the program into two seperate object files. Include both object file names in the linking command.


The compiler is limited to 64K of memory for symbols table. If you receive this error message from Clipper, divide your program in half and compile it into two object files. Specify both object file names to the linker when you prepare the .EXE file.


The compiler has found a DO CASE statement without an ENDCASE.


The compiler has found a DO WHILE that is not terminated by an ENDDO.


Clipper has found a FOR command without the NEXT statement that terminates it.


Clipper has found an IF without a terminating ENDIF.


Clipper does not recognize a command verb or keyword that has been included in your command. Usually, this error is caused by a spelling error.

PLink86 Plus Messages

PLink86 Plus, supplied with Clipper, is a product published by Phoenix Technologies, the same company that produces compatible ROMs for computer manufacturers. Most people simply refer to it as PLink or PLink86. PLink86 Plus is used widely by software delevopper.

PLink86 Plus is included with Clipper because of its capability to produce overlay files efficiently. The copy of PLink86 Plus included with your Clipper package has been modified by Nantucket to be Clipper-specific. Some of the error messages that PLink86 Plus is capable of returning actually should never be seen by a Clipper programmer unless he or she is plugging in assembly language routines and having problems. Each of these error messages is coded by number. If you experience problems with PLink86 Plus, refer to Nantucket technical support for help in resolving your problem. The following discussion centers on the messages most often occuring in the Clipper environment. For a complete listing of the PLink86 Plus error messages, see the Clipper manual.

The messages that are delivered by Plink86 Plus are broken into two categories: warnings and errors.

PLink86-Plus Warning Messages

Warning 3: This error message is triggered when a ‘group’, a set of memory segments that must be able to fit within a 64K frame in memory, exceeds the limit. This warning message happens usually when you are mixing C and assembly language routines within modules for the Clipper application. Assembly language class and segment names must match those used in the high-level language. If not, assembler segments may be assigned to areas of memory that are not within the 64K frame of the ‘group’. This error message may appear also if you name one of your procedures DATA or SYSTEM. These are reserved words; don’t use them.

Warning 4: A file has been specified in the MODULE command but does not appear in the directory.

Warning 7: Usually, this means you have included the .PRG extension with the file name when you access the CL.BAT batch file. PLink86-Plus is trying to link .PRG files instead of .OBJ files because the batch file specified .PRG as the file extensions.

Warning 8: This warning is usually the first indication of a corrupted object file.

Warning 9: This warning also is an early indication of a corrupted object file.

Warning 11: You have declared the same variable to be public more than once. The compiler ignores the second declaration of the public variable and continues to link the file.

Warning 12 or Warning 13: These warnings are triggered usually when an assembly language module you are linking as part of your program makes an errouneous memory assignment.

Warning 14: This message also deals with assembly language routines assigning memory for stack storage. Using PLink86 Plus and .LIB files from different Clipper versions generates this message also.

Warning 19: This usually indicates that you are nearing the limits of memory in linking your application. PLink86 Plus determines that there is not enough memory to store path names for a file that it cannot locate and will prompt you to enter the path name.

PLink86 Plus Error Messages

PLink86 Plus error messages are grouped into categories that have common roots.

Plink86 Plus File Error Messages

The first group deals wiht errors in the input to the Plink86 Plus command, usually dealing with macro-substituted file names.

Error 1 : This error message indicates that @ files are nested more than three deep or that the @ files contain a circular reference.

Error 2 : A DOS error occurred while trying to read the @ file.

Error 3 : The @ file specified cannot be found.

Error 5 : The Plink86-Plus input line exceeds the maximum lengh of 64 characters.

Error 10: This error message indicates an invalid file name. Plink86 Plus has been given a character string that is an illegal DOS file name.

Error 11: A keyword is missing from the .LNK file.

Error 14: This message indicates that PLink86 Plus is expecting an identifier. A section module segment or symbol name is missing from the .LNK file or was mistyped at interactive input time.

Error 15: An equal sign (=) is missing in the .LNK file.

Error 17: No files have been specified to be linked. Check the .LNK file to make sure the file command is present.

PLink86 Plus Work File Error Messages

Plink86 Plus requires at least 256K of memory to work properly. As PLink86 Plus runs out of memory, it opens a scratch file on disk to hold contents of the link temporarily. The following error messages indicate a problem with the work file.

Error 30: A work file cannot be created, probably because of a lack of disk space.

Error 31: A DOS I/O error happened while Plink86 Plus was writing the .EXE file.

Error 32: PLink86 Plus encountered a DOS I/O error as it was reading the work file.

Error 33: A DOS I/O error occured while Plink86 Plus was positioning the pointer within the file.

Error 34: This error message indicates that you have created an application that is too large for PLink86 Plus to handle. Please check the source code for possible inefficiencies. See the section outlining the SWITCH utility in Chapter 9, “Clipper Tools”.

PLink86 Plus Input Object File Error Messages

These errors involve the object files that have been presented to PLink86 Plus. They genereally indicate a corrupted object file that should be recompiled.

Error 41: Premature end of object file. PLink86 Plus is finding an object file that is a different size from that stated by the header at the beginning of the file. This error message can be triggered also by specifying the .PRG extension to the CL.BAT batch file. This error canbe the result also of high-order bits being turned on when you use WordStar in the document mode as an editor. While rare, this cause is possibility.

Error 42: Fatal read error in object file. This error message indicates that the object file is completely unusable.

Error 43: PLink86 Plus cannot find a specified object file. If PLink86 Plus was not invoked with a batch file, it will prompt you for a path name.

PLink86 Plus Output File Error Messages

This group of errors deals with problems in creating the .EXE file itself.

Error 45: A DOS error has occured as PLink86 Plus tried to creat the .EXE file. The disk is either full or possibly write-protected.

Error 46: Invalid output file type. You probably have included in your .LNK file an output command that specifies an invalid extension to the .EXE file.

Error 47: Fatal disk write error. PLink86 Plus was able to creat the file; but as it was writing the file, the disk drive probably was filled.

Error 48: Fatal read error in output file. PLink86 Plus is unable to read the file it has constructed. This is probably due to a hardware error.

Error 49: Cannot close output file. This error is probably caused by a hardware error or a write-protect error.

Error 50: Cannot construct memory map. The memory map cannot be constructed, probably because the disk is full.

PLink86 Plus Miscellaneous Error Messages

Error 51: Undefined symbol exist. This error occurs when you specify a procedure or function in your program, and the linker cannot find object code to construct it. Usually, you have forgotten to include the EXTEND.LIB in your PLink86 Plus command after including a function from EXTEND.LIB in your application.

Error 54: This error indicates there is not enough random access memory on the computer to run PLink86 Plus.

Error 57: This error indicates a problem with OVERLAY.LIB. Recopy the file from your factory disks.

Error 58: You have created a stack segment in an assembly language program that is too large.
PLink86 Plus Program Structure Error Messages

These error are triggered when there are problems within overlay for your application.

Error 80: Overlays are nested too deeply.

Error 81: There are more ENDAREA statements than BEGINAREA statements in your .LNK file.

Error 82: There are more BEGINAREA statements than ENDAREA statements in your .LNK file.

Error 83: Your application is too large, but it can be compiled if you break the application into a load module with overlays.

PLink86 Plus Diagnostic Error Messages

If you receive error messages greater than 200 from PLink86 Plus PLink86 Plus rest assured that your program could not have caused these error messages. Usually, these error messages indicate that the PLink86 Plus file itself has been corrupted. Recopy PLink86 Plus from the factory disk. If the problem repeats, record the error code and whatever information is necassary to produce repeatable results and contact Nantucket Technical Support.


In this chapter, you have seen the source code that produces Clipper’s run-time error messages. You should have some idea of how you might resolve them.

You should also be prepared to decipher error messages and warnings from both Clipper and PLink86 Plus. Don’t forget that most link errors occur simply because you have forgotten to declare a file to the linking process.

If you encounter a stubborn linkage problem with a third-party linker, come back to PLink86 Plus and decipher the error message it delivers. Correct the problem, and relink using the linker of your choice.

16:31:41 18/11/1991