Greg is a consultant and multilanguage developer. He is also the author of the In-Press tool marketed by Dabiwa. He can be contacted on CompuServe at 73747,3112.
Like many software developers, I program in multiple languages. In addition to C/C++, I regularly use Visual Basic, Clipper, FoxPro, and Paradox. After many years of development, my office shelves began to sag under the accumulated weight of third-party support libraries. At last count, I had four communications libraries (one each for Clipper, FoxPro, C, and Visual Basic), multiple printing tools, and a variety of other libraries. After reflecting on the time it takes to learn and remember four or five ways of accomplishing the same thing, I decided to create a language-independent API that generates language-specific libraries. I call it WRAPI, short for "wrapped API."
By inserting a translation layer between the host-language API and the internal functions of a library, WRAPI creates a higher-level access to what has typically been the domain of C and ASM programmers. A C library written with the WRAPI toolkit will find a much wider audience than a C-only library. The number of non-C developers, particularly in the high-level languages, is growing dramatically, and WRAPI provides a way to leverage costly development efforts into this large market.
The host languages WRAPI supports include C, Clipper, FoxPro (DOS and Windows), Visual Basic (DOS and Windows), Pascal, Clarion, Fortran, and many Windows tools via a generic DLL. Certain types of libraries lend themselves to WRAPI. Anything that is currently called an API (such as Microsoft's Mail API or the ODBC API) is a candidate for WRAPI. WRAPI can also be used with universal functions such as mathematics, communications, printing, and multimedia.
WRAPI is proven technology. It's currently the core technology in Dabiwa's In-Press tool, which provides a set of functions for developers who need to produce published-quality output from within host applications. Because In-Press is built from WRAPI, it works with all of the major development languages on the PC platform. With this article, I'm putting WRAPI technology into the public domain, making the source code available electronically through DDJ; see "Availability," page 3.
You usually don't talk in terms of an API when writing a C function library. Such functions are compiled into a LIB and subsequently integrated into an executable whole by a linker. As long as symbol-naming and parameter-passing conventions are uniform among the various components, the executable will indeed execute.
Instead, the term API is generally associated with the high-level languages. FoxPro provides API functions for everything from memory allocation to low-level I/O. Clipper provides API functions for parameter passing. Visual Basic for DOS requires that strings be dereferenced with StringAdress(), arguably an API function.
The term API in the context of WRAPI stands for the interface layer of the host language. In C, that layer is simply the call stack and the C standard library. In reality, there is no API. For the purposes of WRAPI, the term "API" is a fiction of convenience. Throughout the rest of this article, I'll refer to the host API when I wish to reference the interface layer of the target development language.
I use the term "wrap" because WRAPI resolves differences by "wrapping" them in a macro or a data structure. WRAPI provides a mechanism for hiding the various methods of parameter passing that exist among the supported host APIs. It also provides uniform mechanisms for error handling, string manipulation, memory allocation, low-level I/O, and printing. It accomplishes this through a combination of preprocessing tricks and a set of support functions which I'll call the "WRAPI library."
Incidentally, the first wrapping functions I wrote were patterned after the original work of Dabiwa's David Karasek, who used compiler directives (#IFDEF, #ELIF, and so on) and boilerplate code to write functions that conditionally compiled to C, Clipper, FoxPro, and Visual Basic. Dave's work provided a good starting place to further the concepts of multilanguage support. In working with his functions, I realized that most of the compiler directives were required to resolve differences in parameter passing, so that is where I began my attack.
The first task in developing WRAPI was to create the specification for "wrapping" functions--a translation layer that sits between the host API and the underlying "core" function; see Figure 1.
A wrapping function grabs parameters from the host API, type-checks them (for weakly typed languages), calls the core function, traps any error condition, and returns a value to the host API. All serious work is handled by the core function, including the task of domain checking parameters. In the initial iteration of WRAPI, I made the mistake of putting all parameter checking in the wrapping function. It seemed logical at the time, but I subsequently learned that wrapper functions work best when they're free of semantic content.
Traditionally, parameters and return values are placed on the call stack. FoxPro and Clipper don't pass parameters in this manner. Xbase is a weakly typed language, and the mechanisms for passing parameters are structured accordingly. Clipper provides a set of API functions to grab parameters off of the Clipper "Eval" Stack (an internally maintained Clipper stack). FoxPro passes a pointer to a structure which itself points to an array of structures, each of which contains a parameter reference. Both Clipper and FoxPro include a set of functions for returning values through the host API. Listing One (page 89) illustrates the differences among C, Clipper, and FoxPro parameter passing.
Encapsulating the Xbase parameter mechanisms was straightforward. The WRAPI library includes a set of functions for grabbing and type checking Clipper and FoxPro parameters. The trick was reconciling the call-stack method of parameter passing with the explicit parameter grabbing of the Xbase languages. After some fiddling, I came up with the function template in Example 1, which works uniformly well for both.
All uppercase keywords are WRAPI macro definitions. Many, such as TYPE_?, are keyed to a basic data type. WRAPI supports six such data types: C, char; S, string; B, Boolean; I, short int; L, long int; and D, double float.
Thus the macro TYPE_I indicates that the wrapping function returns a short integer, while the macro GRAB_S() grabs a string-type parameter from the API. Listing Two (page 89) is a simple wrapping function written with WRAPI macros. The function has been stripped of the error-handling mechanisms so that you can better understand the basics of parameter wrapping. The corresponding translations to the C, Clipper, and FoxPro APIs are illustrated in Listing Three (page 89).
The form of the wrapping function may seem a little bizarre at first, but it is a bit of preprocessing trickery that greatly increases code readability.
After reconciling the different parameter-passing conventions, the next step was to deal with strings. In C, we generally work with zstrings (short for "zero-terminated strings"). Many of the host languages, including Visual Basic for DOS, FoxPro, Clarion, and Pascal, pass buffered strings of one type or another. My first inclination was to convert all strings to zstrings at the wrapping-function level.
This approach would have allowed core functions to work exclusively with zstrings, the advantages of which are obvious. The strategy was simple enough: Allocate a chunk of memory one byte longer than the buffered string, copy the buffered string, and zero terminate it. But some APIs provide precious little available heap, so a function that receives a 48K string would have to burn up another 48K of heap space simply to add a zero terminator.
The alternative was a data structure that could handle the various string types with equal aplomb. This structure, the WSTR, is shown in Figure 2. The PREP_S() and GRAB_S() macros automatically create and load a WSTR structure with the necessary components of the API string. All API strings are resolved to an address stored in the cp component and a string length stored in the uiLen component. Some APIs pass strings via a handle. In such cases, the handle is stored in the ulHnd component.
The WSTR approach adds a level of complexity to writing core functions. Most would agree that it is easier to work with zstrings than a buffered hybrid such as WSTR. Unfortunately, the wrapping macros worked much more efficiently using the WSTR, and that tipped the balance. Listing Four (page 89) shows a simple string-manipulation function. The associated C, Clipper, and FoxPro translations are shown in Listing Five (page 89). These translations are quite involved and are beyond the scope of this article.
Once I decided to wrap strings in a WSTR structure, it became necessary to write a set of manipulation functions. The WRAPI library provides a basic set of WSTR functions; see Table 1.
String manipulation highlights one of the bigger drawbacks of WRAPI development. There are effectively no C standard-library functions. Staples of the C programmer such as strcpy(), sprintf(), and strtok() simply don't exist in Clipper, FoxPro, or Visual Basic. However, many APIs provide alternate functions. Consider memcpy(): The FoxPro API includes a function _MemCpy(), and the Clipper standard library includes an undocumented internal _bcopy().
To make WRAPI more C-friendly, I developed a strategy of wrapping C standard functions with a macro of the same name in upper case. Thus, memcpy() became MEMCPY(). The macro translates to the C standard function or to the corresponding API function. Where an API didn't provide the equivalent C standard function, I wrote one. Table 2 lists the C standard functions that I include with WRAPI. You may wish to add to the list. My goal was to minimize dependence on the C standard library, so the list remains quite short.
You may notice the low-level I/O functions deviate slightly from the strategy of wrapping standard functions in uppercase macros. The open() function became F_OPEN_RW(), F_OPEN_RO(), and F_OPEN_WO(). Likewise, lseek() became F_GOBOF(), F_GOEOF(), F_GOTO(), and so on. Low-level I/O flags differed among the supported APIs, and the easiest solution was to eliminate them entirely by creating multiple macros.
The next order of business was to provide a uniform method of memory allocation. The malloc() function cannot be used in all APIs. Again, the main culprits are the Xbase dialects. Both allocate memory using an API function that returns a handle. The handle is easily decoded to an address. When it is time to free the memory, it is the handle that must be supplied, not the decoded address. Thus, the handle must be retained somehow.
The WRAPI library includes ALLOC() and FREE() macros. ALLOC() increases the size of each memory allocation by six bytes and returns an address six bytes in from the actual start of allocated memory. The first six bytes are used to store a 4-byte handle (if needed) and a 2-byte length. This allows FREE() to function transparently.
Under DOS, printing is handled through low-level I/O functions. This allows printing to be directed to either a device or a file. Under Windows, printing to a file is also accomplished through low-level I/O, but printing to a print device requires calls to GDI functions.
In designing WRAPI, I adhere to the DOS printing model. Four constants are used to specify the print devices PRN and LPT1 through LPT3 (no support for COM devices other than a redirected PRN). These constants all have negative values. This allows a single function to be used when selecting a print destination. Either a valid device constant or a file handle (a positive number) can be passed. WRAPI then decodes this information into the appropriate print destination.
Like many things in WRAPI, the print destination requires a wrapping structure. Figure 3 shows the WPRN structure. Under DOS it is sufficient to store the file handle and a Boolean flag indicating that output is directed to a standard print device. Under Windows it is also necessary to store the HDC (handle to a device context) and the state of the spool flag in WIN.INI. (WRAPI automatically turns the spooler off before printing and restores it to its previous state when finished.)
The WRAPI print functions are listed in Table 3. After receiving the desired print destination (in the form of a signed integer) from the host application, iWPrnStart() must be called to decode the destination into a WPRN structure. If the destination is a standard device, iWPrnStart() opens the device under DOS, or creates an HDC under Windows. When printing is completed, iWPrnEnd() must be called to clean up. Failure to call iWPrnEnd() under Windows will result in an orphaned HDC--a very bad idea.
The remaining print functions are used to send data directly to the print device. No effort is made to format output under GDI. All output is sent via the GDI Escape() function as DEVICEDATA. This effectively turns Windows printing into DOS printing. Incidentally, this is also the reason the print spooler must be turned off.
The error handling in WRAPI reflects my belief that the return value of a function should not be the source of error information. WRAPI provides an error-trapping mechanism that delivers the error to the host API in the most appropriate manner. Table 4 lists the different error mechanisms employed by WRAPI.
The biggest compromise in error handling is caused by host APIs that don't have a standard error-trapping mechanism. In such cases, it is necessary for the host application to pass a function reference to the core library. The core library must then make that function reference available to the WRAPI error-trapping mechanism. The error trap simply calls the referenced function. Unfortunately, this destroys an otherwise clean separation between the host, the wrapping, and the core.
This shortcoming of the error system is best illustrated with an example. In-Press provides two functions: IpErrFName(), which takes the name of a FoxPro function designated as the error handler; and IpErrFPtr(), which takes the address of a C function (cast as a long) designated as the error handle. Failure to register an error function with IpErrFName() under FoxPro or IpErrFPtr() under C effectively defeats In-Press error handling. In effect, the application is blind to In-Press errors.
Error handling blurs the line between the core and the wrapping. All core libraries must follow certain error conventions and provide a standard set of error macros to the wrapping functions. These macros, listed in Table 5, act as a call back into the core library. Listing Six (page 90) is an In-Press wrapping function and its corresponding C translation. Note the references to the In-Press error-handling internals.
A WRAPI library design is constrained by several factors. For one thing, the names of wrapping functions are limited to ten characters (as are the names of any support constants). This limitation is imposed by FoxPro, and there is no way around it. More importantly, the data types are limited to the six basic ones listed earlier: character, string, Boolean, short integer, long integer, and double float.
In C, there is a tendency to keep related data in a structure. This works nicely because the structure can be passed by reference. WRAPI precludes that type of interaction between a core library and the host API. How then do you structure a useflibrary if data moves in and out as simple data types?
My solution was to store the data structure internally and provide discrete access to each component with a GET/SET function. For those unfamiliar with the term GET/SET, it is a function that can simultaneously change the state of an internal value while returning its previous state. A GET/SET function can also query the state of an internal value without actually changing it by passing a NIL value.
Although you don't need to use GET/SET functions in the design of a WRAPI library, they are quite useful. WRAPI includes logic for NIL values and a sample GET/SET engine.
Table 6 lists all of the tools needed to develop and test a WRAPI library. It is no small undertaking. In addition to purchasing and installing the listed products, you will want a mechanism for automating the build process. A set of batch files and a sample makefile are included with the source diskette to help you get started.
Any library requires a bit of planning. WRAPI simply adds to the process. The general scheme for developing a WRAPI library is:
It can take time to test a WRAPI library. Each change to a library must be tested under the numerous languages. It is not uncommon to spend an entire afternoon checking the results of a function that took only one hour to write. WRAPI certainly doesn't lend itself to an ad hoc development cycle. After you gain familiarity with WRAPI development you learn where to take some shortcuts. (If it works under Clipper and FoxPro for Windows, for example, it probably works under everything else.)
WRAPI development seems to yield robust libraries. The various host APIs expose different types of problems. I found that general debugging was easiest in the DOS environment using C or Clipper as the host language. Once all of the obvious errors were exorcised, it was on to the protected-mode languages, either FoxPro for Windows or Visual Basic for Windows, to find the memory violations. I was able to use the CodeView debuggers quite successfully.
WRAPI is a work in progress and there are shortcomings to the technology that you may want to improve. For instance, parameters must be passed by value, although it should be possible to wrap parameters passed by reference. WRAPI has no mechanism for wrapping interrupts. The general lack of C standard library functions will certainly impede conversion of existing libraries to WRAPI. Also, WRAPI has not been extended to OS/2, NT, or UNIX.
There are several inherent limitations to WRAPI. The ten-character limit on symbol names can make function and support constants a bit terse.
The technology is not quite a "black box." Thus, the WRAPI developer is required to understand the APIs of the supported languages. And WRAPI functions are by their nature limited to "driver" type applications. Proprietary hooks into a particular O/S or a development environment defeat the purpose of WRAPI.
Even with these limitations, WRAPI is a good starting point if you need to support multiple languages. The source files include everything you need. I will maintain and update WRAPI on the CompuServe DDJ Forum. I hope you will join me there as I answer questions and attempt to improve the technology based on the suggestions of other WRAPI developers.
/* WSTR typedef */
typedef struct { // WRAPPED-API STRING STRUCTURE
char *cp ; // Actual pointer to the string
ushort uiLen ; // Length of string
ulong ulHnd ; // Host API handle if applicable
} WSTR ;
TYPE_? <Function Name> ( PARMLIST ) {
DECL_? ( <Return Value> ) ;
PREP_? ( <Parameter #> , <Parameter Name> ) ; ...
SET_ERR_NONE ( ) ;
GRAB_? ( <Parameter #) , <Parameter Name> ) ; ...
if ( IS_ERR_NONE ( ) )
<Return Value> = <Core Function> (
<Parameters> ) ;
SET_ERR_LOC ( "<Function Name>" ) ;
TRAP_ERR ( ) ;
SEND_? ( <Return Value> )
}
Function Description ------------------------------------------------------------------------------- short szwcmp ( char*, WSTR* ) compares zstring to WSTR short swzcmp ( WSTR*, char* ) compares WSTR to zstring short swwcmp ( WSTR*, WSTR* ) compares WSTR to WSTR void szwcpy ( char*, WSTR*, ushort) copies WSTR to zstring void swzcpy ( WSTR*, char* ) copies zstring to WSTR void swwcpy ( WSTR*, WSTR* ) copies WSTR to WSTR
/* WPRN typedef */
typedef struct { // WRAPPED PRINT DEVICE STRUCTURE
bool bStd ; // a STD print device?
fhandle fh ; // handle to a DOS file/device
#ifdef _FAMILY_DLL
HDC hdc ; // handle to a device context
bool bSpool ; // indicates that spooler was hooked up
#endif
} WPRN ;
WRAPI function C standard Meaning --------------------------------------------------------- STRCPY strcpy Copy zstring STRCMP strcmp Compare zstrings STRLEN strlen Length of zstring MEMCPY memcpy Copy memory block MEMCMP memcmp Compare memory blocks MEMSET memset Fill memory block with char ATOI atoi zstring to short ATOL atol zstring to long ATOF atof zstring to double ITOA itoa Short to zstring LTOA ltoa Long to zstring UTOA utoa Unsigned short to zstring ULTOA ultoa Unsigned long to zstring FTOA Double to zstring ROUND Round double to decimal place F_OPEN_RW open Open device for read/write F_OPEN_RO open Open device for read only F_OPEN_WO open Open device for write only F_CREATE create Create file F_READ read Read from device F_WRITE write write to device F_GOBOF lseek Go to beginning of file F_GOEOF lseek Go to end of file F_GOTO lseek Go to offset in file F_GOBACK lseek Go to negative offset from EOF F_POS lseek Position pointer in file F_MOVE lseek Move pointer through file F_CLOSE close Close device
Function Description ------------------------------------------------------------------------------------ short iWPrnStart ( WPRN*, short ) initialize print context short iWPrnEnd ( WPRN* ) end print context bool bWPrnC ( WPRN*, char ) print a char bool bWPrnSZ ( WPRN*, char* ) print a zstring bool bWPrnSB ( WPRN*, char*, ushort ) print a buffer bool bWPrnWSP ( WPRN*, WSTR* ) print a WSTR
Host API Mechanism ------------------------------------------ C (DOS) Call to a function pointer Clarion Error posted in queue Clipper S'87 Call to MISC_ERROR() Clipper 5.x Error event Generic DLL Error message FoxPro Call to a named function Visual Basic ON ERROR event
Error Macro Description ---------------------------------------------------------------------------------------- SYSTEM_NAME() Returns name of WRAPI library (for example, "In-Press") IS_ERR_NONE() Returns Boolean True if no error condition IS_ERR_ERR() Returns Boolean True if error in error handler GET_ERR_CODE() Returns the current error code GET_ERR_FPTR() Returns pointer to a C function designated as the error handler GET_ERR_FNAME() Returns name of FoxPro function designated as error handler GET_ERR_HOST() Returns host API error designator (used for Visual Basic) GET_ERR_LOC() Returns current error location (name of wrapping function) GET_ERR_TEXT(i) Returns the text associated with error code i SET_ERR_NONE() Sets error code for: no error condition SET_ERR_TYPE() Sets error code for: invalid data type SET_ERR_COUNT() Sets error code for: invalid parameter count SET_ERR_ALLOC() Sets error code for: unable to allocate memory SET_ERR_OUT() Sets error code for: problem writing to output device SET_ERR_GDI_CMD() Sets error code for: problem writing to GDI SET_ERR_LOC(sz) Sets the current location (name of wrapping function) CALL_ERR_VBD() Invokes Visual Basic for DOS error bridge CALL_ERR_CLIP4() Invokes Clipper S'87 error bridge CALL_ERR_MSG() Invokes function to post a Windows error message
Tool Needed for ------------------------------------------------------------------ Borland C 3.x Borland C Microsoft C 5.1 Clipper (MSC 7.0 can be substituted) Microsoft C 7.0 Microsoft C, generic DLL, Visual Basic TopSpeed C TopSpeed, Clarion Watcom C 9.x Watcom C, FoxPro Clarion Testing (order with TopSpeed) Clipper S'87 Testing (if you can find it) Clipper 5.2x Testing Paradox for Windows Testing Visual Basic for DOS Testing Visual Basic for Windows Testing Microsoft CodeView & NMake Debugging and making (comes with MSC)
/* C Parameters passed on call stack */
double SquareIt ( double dValue ) {
return ( dValue * dValue ) ;
}
/* Clipper Parameters aquired through API functions */
CLIPPER SquareIt ( void ) {
double dValue = 0.0 ;
// if one parameter of type numeric
if ( _parinfo ( 0 ) == 1
&& _parinfo ( 1 ) == NUMERIC
)
{
// grab parameter from Clipper
dValue = _parnd ( 1 ) ;
}
// send return value to Clipper
_retnd ( dValue * dValue ) ;
return ;
}
/* FoxPro Parameters passed in a data structure */
void SquareIt ( ParamBlk *pParmBlk ) {
double dValue = 0.0 ;
// if one parameter of type numeric
if ( (*pParamBlk).pCount == 1
&& (*pParamBlk).p[0].val.ev_type == 'N' )
)
{
// grab parameter
dValue = (double) (*pParamBlk).p[0].val.ev-real ;
}
// send return value to FoxPro
_RetFloat ( dValue * dValue , 12 , 4 ) ;
return ;
}
/* Setup for wrapping functions */
#ifdef _FAMILY_C // parameters passed on call stack
#define SquareIt(PARMLIST) SquareIt ( double dValue )
#ednif
#ifdef _API_FOX // FoxPro requires a global data structure
FoxInfo myFoxInfo[] =
{ { "SQUAREIT" , (FPFI) SquareIt , 1 , "N" } } ;
FoxTable _FoxTable =
{ (FoxTable*) 0
, sizeof(myFoxInfo) / sizeof(FoxInfo)
, myFoxInfo
} ;
#endif
/* Prototypes */
TYPE_D SquareIt ( PARMLIST ) ;
double _SquareIt ( double ) ;
/* Wrapping function (without error handling) */
TYPE_D SquareIt ( PARMLIST ) { // ( dValue )
// declare return value (and any other local values)
DECL_D ( dReturn ) ;
// prepare to accept parameter(s)
PREP_D ( 1 , dValue ) ;
// grab parameter(s) from host API
GRAB_D ( 1 , dValue ) ;
// call core function
dReturn = _SquareIt ( dValue ) ;
// send return value to host API
SEND_D ( dReturn ) ;
}
/* Core function */
double _SquareIt ( double dValue ) {
return ( dValue * dValue ) ;
}
/* C translation of wrapping function */
double SquareIt ( double dValue )
{
// DECL_D ( dReturn ) ;
double dReturn = 0.0 ;
// PREP_D ( 1 , dValue ) ;
;
// GRAB_D ( 1 , dValue ) ;
;
// call to core function
dReturn = _SquareIt ( dValue ) ;
// SEND_D ( dReturn ) ;
return dReturn ;
}
/* Clipper translation of wrapping function */
void SquareIt ( void )
{
// DECL_D ( dReturn ) ;
double dReturn = 0.0 ;
// PREP_D ( 1 , dValue ) ;
double dValue = 0.0 ;
// GRAB_D ( 1 , dValue ) ;
dValue = dAPIgrab ( 1 ) ;
// call to core function
dReturn = _SquareIt ( dValue ) ;
// SEND_D ( dReturn ) ;
_retnd ( dValue ) ;
return ;
}
/* FoxPro translation of wrapping function */
void FAR SquareIt ( ParamBlk* gpFoxParm )
{
// DECL_D ( dReturn ) ;
double dReturn = 0.0 ;
// PREP_D ( 1 , dValue ) ;
double dValue = 0.0 ;
// GRAB_D ( 1 , dValue ) ;
dValue = dAPIgrab ( 1 , gpFoxParm ) ;
// call to core function
dReturn = _SquareIt ( dValue ) ;
// SEND_D ( dReturn ) ;
_RetFloat ( dReturn , 12 , 4 ) ;
return ;
}
/* Setup for wrapping functions */
#ifdef _FAMILY_C // parameters passed on call stack
#define FlipIt(PARMLIST) FlipIt ( APISTR wspText )
#ednif
#ifdef _API_FOX // FoxPro requires a global data structure
FoxInfo myFoxInfo[] =
{ { "FLIPIT" , (FPFI) FlipIt , 1 , "C" } } ;
FoxTable _FoxTable =
{ (FoxTable*) 0
, sizeof(myFoxInfo) / sizeof(FoxInfo)
, myFoxInfo
} ;
#endif
/* Prototypes */
TYPE_S FlipIt ( PARMLIST ) ;
WSTR* _FlipIt ( WSTR* ) ;
/* Wrapping function (without error handling) */
TYPE_S FlipIt ( PARMLIST ) { // ( wspText )
// declare return value (and any other local values)
DECL_S ( wspReturn ) ;
// prepare to accept parameter(s)
PREP_S ( 1 , wspText ) ;
// grab parameter(s) from host API
GRAB_S ( 1 , wspText ) ;
// call core function
wspReturn = _FlipIt ( wspText ) ;
// send return value to host API
SEND_S ( wspReturn ) ;
}
/* Core function */
WSTR* _FlipIt ( WSTR* wspText )
{
// storage for return value
static char szaReturn [ 256 ] ;
// WSTR wrapper for return value
static WSTR wsReturn ;
// pointers for string flip loop
char *cpOld , *cpNew ;
// counter for string flip loop
ushort u ;
// point return WSTR at return buffer
wsReturn.cp = szaReturn ;
// set return length to incoming length
wsReturn.uiLen = *wspText.uiLen ;
// limit return length to size of return buffer
if ( wsReturn.uiLen > ( sizeof ( szaReturn ) - 1 ) )
wsReturn.uiLen = ( sizeof ( szaReturn ) - 1 ) ) ;
// set up loop variables
u = wsReturn.uiLen ;
cpNew = szaReturn ;
cpOld = *wspText.cp + u - 1 ;
// flip old into new
while ( u-- )
*cpNew++ = *cpOld-- ;
// zero terminate regardless of host API
*cpNew = '\0' ;
// return WSTR pointer
return ( &wsReturn ) ;
}
/* C translation of wrapping function */
char* FlipIt ( char* wspText )
{
// DECL_S ( wspReturn ) ;
WSTR wsReturn ; WSTR *wspReturn = &wsReturn ;
// PREP_S ( 1 , wspText ) ;
WSTR wsBuff1 ;
// GRAB_S ( 1 , wspText ) ;
if ( wspText == NULL )
{
wsBuff1.cp = (char*) "\xFF" ; // defined as NIL
wsBuff1.uiLen = (ushort) 1 ;
}
else
{
wsBuff1.cp = (char*) wspText ;
wsBuff1.uiLen = (ushort) strlen ( wsBuff1.cp ) ;
}
wsBuff1.ulHnd = 0L ;
wspText = &wsBuff1 ;
// call to core function
wspReturn = _FlipIt ( wspText ) ;
// SEND_S ( wspReturn ) ;
return *wspReturn.cp ;
}
/* Clipper translation of wrapping function */
void FlipIt ( void )
{
// DECL_S ( wspReturn ) ;
WSTR* wspReturn = (WSTR*) (void*) 0L ;
// PREP_S ( 1 , wspText ) ;
WSTR wsBuff1 ; WSTR* wspText = &wsBuff1 ;
// GRAB_S ( 1 , wspText ) ;
_bcopy ( wspText , wspAPIgrab ( 1 ) , sizeof ( WSTR ) ) ;
// call to core function
wspReturn = _FlipIt ( wspText ) ;
// SEND_S ( wspReturn ) ;
_retc ( *wspReturn.cp ) ;
return ;
}
/* FoxPro translation of wrapping function */
void FAR FlipIt ( ParamBlk *gpFoxParm )
{
// DECL_S ( wspReturn ) ;
WSTR* wspReturn = (WSTR*) (void*) 0L ;
// PREP_S ( 1 , wspText ) ;
WSTR wsBuff1 ; WSTR* wspText = &wsBuff1 ;
// GRAB_S ( 1 , wspText ) ;
_MemCpy ( wspText
, wspAPIgrab ( 1 , gpFoxParm )
, sizeof ( WSTR )
) ;
// call to core function
wspReturn = _FlipIt ( wspText ) ;
// SEND_S ( wspReturn ) ;
_RetChar ( wspReturn && *wspReturn.cp
? *wspReturn.cp
: ""
) ;
while ( (*gpFoxParm).pCount )
if ( (*gpFoxParm).p[--(*gpFoxParm).pCount].val.ev_type == 'C' )
_HUnLock ( (*gpFoxParm).p[(*gpFoxParm).pCount].val.ev_handle ) ;
return ;
}
/* An In-Press wrapping function (with error handling) */
TYPE_I IpBoStyle ( PARMLIST ) { // ( [iStyle] )
DECL_I ( iReturn ) ;
PREP_I ( 1 , iStyle ) ;
SET_ERR_NONE ( ) ;
GRAB_I ( 1 , iStyle ) ;
if ( IS_ERR_NONE )
iReturn = GETSET_I ( HND_BO_STYLE , iStyle ) ;
SET_ERR_LOC ( "IpBoStyle" ) ;
TRAP_ERR ( ) ;
SEND_I ( iReturn ) ;
}
/* The C translation of an In-Press wrapping function */
short IpBoStyle ( iStyle ) {
// DECL_I ( iReturn ) ;
short iReturn ;
// PREP_I ( 1 , iStyle ) ;
;
// SET_ERR_NONE ( ) ;
( _iIpErrCode ( 0 ) ) ;
// GRAB_I ( 1 , iStyle ) ;
;
// if ( IS_ERR_NONE ) ...
if ( ( _iIpErrCode ( -32767 ) == 0 ) )
iReturn = ( * ( (short*) _vpIpSet ( 80 + 512 , &iStyle ) ) ) ;
// SET_ERR_LOC ( "IpBoStyle" ) ;
( _wspIpErrLoc ( szaaswsp ( "IpBoStyle" ) ) ) ;
// TRAP_ERR ( ) ;
if ( ! ( _iIpErrCode ( -32767 ) == 0 ) )
vErrTrapC ( ) ;
// SEND_I ( iReturn ) ;
return iReturn ;
}
Copyright © 1994, Dr. Dobb's Journal