/************************************************************************ ********* "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; } } }