/************************************************************************
********* "C" PRINTF exported to AutoLISP. *******
** **
* Created (C.) by Vladimir Nesterovsky <vnestr@netvision.net.il> 1996.*
* All Rights Reserved *
* You may use this function for any non-commercial purpose *
* provided that you keep this notice complete and unaltered *
* here and in all your derived works. *
* For any commercial use you MUST first contact me to get a *
* permission (basically speaking some support fee will be required). *
* USE IT AT YOUR OWN RISK. NO WARRANTIES ARE GIVEN WHATSOEVER. *
* *
************************************************************************/
//
// The basic idea is to set up some memory block on heap
// and fill it up with actual arguments like compiler
// does while passing args on stack, then to call _vbprintf() --
// which expects them to be in va_list form, so we need to mimic it.
// (actually, it's about default integer promotion of arguments in C).
//
//
// To be called from LISP as:
// (vpn_printf format values ... )
// format := format string for "C" printf()
// val := number | str | [3d]point | nil | T
// point -> X and Y will be processed as doubles
// 3Dpoint -> X Y and Z will be processed as doubles
//
// All other types encountered will be skipped (ignored).
// For nil or T you must specify "%s" in format string,
// as it'll be converted to string, " NIL " or " T ".
// Other values and format string are passed to C "AS IS"
// without any evaluation or analysis, so you must know
// what you are doing (and what your compiler will).
//
// CAUTION -- if (vpn_printf ... '(1 2) 2.3 4 "s") is called, then a 2-int
// pair is translated here to 2DPOINT automatically by ADS thus
// creating 2 doubles, but user may think of it as of 2 integers
// and specify "%d %d" in format_str -- leading to error.
//
// e-mail me with any questions/comments/suggestions you may have.
//
// Known bugs and limitations:
//
// Can't use %n (of course).
// Also note that all INTs are passed as SHORTs to ADS (i.e. truncated).
//
// Don't forget to use "%f %f %f" for point and "%hd" for short int,
// { (vpn_printf "%x %hx" -1 -1) returns "ffffffff ffff" }
// as all shorts are (signed) promoted back to int inside -- so
// '0x00ab0123' is passed as '0x0123' to ADS and then promoted to
// '0x00000123' inside this function, and '0xabcda123' becomes
// '0xa123' and then '0x1111a123'
//
// {{ actually all types used here -- short(converted to int automatically),
// long, double and char* -- are all multiples of sizeof(int)==4 in size,
// so all this rounding macros (see later) are not needed
// except for to be sure it's portable (?..) }}
//
//
// If you find [and fix] any bug, please send me your code.
//
// Bug fixes and enhancements:
//
// 1. fixed signed int promotion of short { (vpn_printf "%d" -1)
// would return "65535" -- now both "%d" and %hd" return "-1" }
// 2. More codes to be processed (you may add yours more easily).
// If you do add, send'em to me also, please.
// 3. Added support for MSVC++ compilation - 02/05/97 -
//
/* Build it (in Watcom C/C++ 10.5) with this makefile:
c_printf.exp : c_printf.obj
wlink system ads libfile adsstart lib wcads file c_printf
c_printf.obj : c_printf.c
wcc386 /3s /fpi87 c_printf.c
Or use it in your MSVC++ project
*/
#include <adslib.h>
#include <stdio.h>
#include <stdarg.h>
// va_list is memory block on stack for retrieving args
// define heap_va_list to set'em up on heap
typedef char *heap_va_list[2]; // use the second to keep starting pointer
//round up to {sizeof(int)=WORD} boundary
#define va_round_up(a) \
(((a)+(sizeof(int)-1))&~(sizeof(int)-1))
#define va_sizeof(type) \
va_round_up(sizeof(type))
#define heap_va_start(ap,p) \
(ap)[1]=(ap)[0]=(char*)(void*)va_round_up((unsigned)p)
#define heap_va_arg(ap,type,val) \
*(type*)((ap)[0])=val; (ap)[0]+=va_sizeof(type)
#ifndef _WIN32
#define heap_va_end(ap,va) \
(va)[0]=(ap)[1]; (ap)[1]=(ap)[0]=0
#else
#define heap_va_end(ap,va) \
(va)=(ap)[1]; (ap)[1]=(ap)[0]=0
#endif
/* -- to be used as: --
heap_va_list vaheap;
va_list vaargs;
void* p0 = calloc(...);
heap_va_start (vaheap, p0);
while(...)
{
heap_va_arg(vaheap, int, 3);
heap_va_arg(vaheap, double, 3.3);
// etc.
}
heap_va_end(vaheap,vaargs); // set vaargs and end vaheap
_vbprintf(sbuf, sbuf_size, format, vaargs); // use vaargs
ads_retstr( sbuf );
free(p0);
*/
// Define here translation macros for types to be processed.
// Add more here ...
#define STR_CASE \
case RTSTR: \
case 0: \
case 2: \
case 1: \
case 1000: \
case 1002: \
case 8: \
case 5: \
case 7
#define REAL_CASE \
case RTREAL: \
case RTANG: \
case RTORINT: \
case 50: \
case 40: \
case 1040
#define SHORT_CASE \
case RTSHORT: \
case 1070
#define LONG_CASE \
case RTLONG: \
case RTENAME: \
case RTPICKS: \
case 1071
#define _2DPOINT_CASE \
case RTPOINT
#define _3DPOINT_CASE \
case RT3DPOINT: \
case 10: \
case 11: \
case 12: \
case 13: \
case 210: \
case 1010
// add more codes as you wish ...
/*-----------------03-15-96 09:31pm-----------------
int vpn_printf()
"C" printf exported to AutoLISP
arguments expected:
FORMAT string
[any arguments]
--------------------------------------------------*/
int vpn_printf()
{
struct resbuf *rbp, *args;
char* format; // format for _vbprintf()
void* p0; // buffer to mimic va_list on heap
heap_va_list vaheap; // heap_va_list to set it up
unsigned len=4*sizeof(int); // initial extra space
va_list vaargs; // to be passed to _vbprintf()
// buffer to get the result
unsigned const sbuf_size = 512; // it's 508 max for ads_retstr()
static char sbuf[512]; // in my R12 version (undocumented !?).
ads_retnil();
args = ads_getargs();
// analyze args list and extract from it format and arguments
if ( !args || args->restype != RTSTR )
{
ads_printf(" to be called with FORMAT [ARGS ...]");
return RSRSLT;
}
format = args->resval.rstring;
rbp = args; // iterator pointer
while ( rbp = rbp->rbnext ) // find out how much to allocate
{
switch ( rbp->restype )
{
case RTT: // NB! to print as string: " T"
case RTNIL: // or " nil "
STR_CASE:
len+=va_sizeof(char*);
break;
SHORT_CASE:
len+=va_sizeof(/*short*/int); // promote short to int
break;
LONG_CASE:
len+=va_sizeof(long);
break;
REAL_CASE:
len+=va_sizeof(double);
break;
_2DPOINT_CASE: // 2 doubles
len+=2*va_sizeof(double);
break;
_3DPOINT_CASE: // 3 doubles
len+=3*va_sizeof(double);
break;
default:
break; // ignore all other types( parens etc. )
}
}
// go back
rbp = args->rbnext;
if ( !rbp ) // format was the only argument
{
ads_retstr(format);
return RSRSLT;
}
// allocate buffer to be filled by arguments
// and used as va_arg stack by _vbprintf()
p0 = calloc( len+256, sizeof(char)); // some more extra space for errors
// to be forgiven at run time
if ( !p0 )
{
ads_fail( "VPN_PRINTF: NO MEMORY" );
return RSRSLT;
}
// fill in buffer with actuall arguments.
// I need to place them on heap like they would be
// on stack -- WORD aligned (heap_va_arg macro will do this).
heap_va_start( vaheap, p0 );
while ( rbp )
{
switch ( rbp->restype )
{
case RTT: // !! must be printed out as STRING: %s
heap_va_arg(vaheap, char*, " T ");
break;
case RTNIL: // !! must be printed out as STRING: %s
heap_va_arg(vaheap, char*, " NIL ");
break;
STR_CASE:
heap_va_arg(vaheap, char*, rbp->resval.rstring);
break;
REAL_CASE:
heap_va_arg(vaheap, double, rbp->resval.rreal);
break;
SHORT_CASE: // short->int *signed* promotion [by compiler]
heap_va_arg(vaheap, /*short*/int, (int)(rbp->resval.rint));
break;
LONG_CASE:
heap_va_arg(vaheap, long, rbp->resval.rlong);
break;
_2DPOINT_CASE:
heap_va_arg(vaheap, double, rbp->resval.rpoint[0]);
heap_va_arg(vaheap, double, rbp->resval.rpoint[1]);
break;
_3DPOINT_CASE:
heap_va_arg(vaheap, double, rbp->resval.rpoint[0]);
heap_va_arg(vaheap, double, rbp->resval.rpoint[1]);
heap_va_arg(vaheap, double, rbp->resval.rpoint[2]);
break;
default:
break; // from switch; continue while.
}
rbp=rbp->rbnext;
}
// simulated stack is built
heap_va_end(vaheap,vaargs);
// HERE ALL THE WORK IS DONE.
// CHECK YOUR COMPILER DOCUMENTATION FOR ALL THE MEANINGS
// OF FORMATTING CODES.
#ifndef _WIN32
_vbprintf( sbuf, sbuf_size, format, vaargs );
#else
_vsnprintf( sbuf, sbuf_size, format, vaargs );
#endif
va_end(vaargs); // like stdargs.h do
free(p0); // free this 'simulated stack' buffer
ads_retstr( sbuf ); // return the result
// for really large formatted strings, the other trick may be done --
// store result directly in symbols by ads_putsym().
return RSRSLT;
}
// That's it!
//
///// Standard ADS interface /////
// Table of exported ADS functions
struct ftblentry
{
char *name;
int (*fptr)();
}
exfuncs[] = {
{ "vpn_printf", vpn_printf },
{ 0, 0 }, //THE LAST - THE MUST
{ "\n==================================================",0 },
{ "\n==== (C.) by Vladimir Nesterovsky, 1996. ======",0 },
{ "\n======== email: vnestr@netvision.net.il =======",0 },
{ "\n=============== All rights reserved ==============",0 },
{ "\n==================================================",0 }
};
int loadfuncs() // LOADFUNCS -- Define external functions with AutoLISP.
{
int i;
for (i = 0; exfuncs[i].name != 0; i++)
{
if ( ads_defun( exfuncs[i].name, i) != RTNORM )
return RTERROR;
if ( ads_regfunc( exfuncs[i].fptr, i) != RTNORM )
return RTERROR;
}
return RTNORM;
}
void main( int argc, char *argv[])
{
int rqst;
short rscode = RSRSLT; // This is the default result code
ads_init(argc, argv); // Initialize the interface
for ( ;; ) {
if ((rqst = ads_link(rscode)) < 0) {
printf( "C_PRINTF: bad status from ads_link() = %d\n", rqst);
exit(1);
}
rscode = RSRSLT; // Default return value
// Check for the following cases here
switch (rqst) {
case RQXLOAD:
ads_printf( "\nADS Application C_PRINTF "
"(C.) by Vladimir Nesterovsky ..");
rscode = (loadfuncs()==RTNORM) ? RSRSLT : RSERR;
if( rscode == RSRSLT)
{
static already_loaded = 0;
if( !already_loaded )
ads_printf(". loaded.\n");
else
ads_printf(". reloaded.\n");
already_loaded = 1;
ads_printf(
"\n=================================================="
"\n==== (C.) by Vladimir Nesterovsky, 1996. ======"
"\n======== email: vnestr@netvision.net.il ======="
"\n=============== All rights reserved =============="
"\n==================================================");
}
else
ads_printf(". failed to load.\n");
break;
case RQSUBR:
break;
case RQXUNLD:
ads_printf( "ADS Application C_PRINTF "
"(C.) by Vladimir Nesterovsky unloaded.\n");
break;
case RQSAVE:
break;
case RQQUIT:
break;
case RQEND:
break;
default:
break;
}
}
}