/* * $Id: expPrint.c,v 4.19 2007/07/04 20:51:11 bkorb Exp $ * * Time-stamp: "2007-07-04 11:19:37 bkorb" * Last Committed: $Date: 2007/07/04 20:51:11 $ * * The following code is necessary because the user can give us * a printf format requiring a string pointer yet fail to provide * a valid pointer, thus it will fault. This code protects * against the fault so an error message can be emitted instead of * a core dump :-) * * This file is part of AutoGen. * AutoGen copyright (c) 1992-2007 by Bruce Korb - all rights reserved * * AutoGen is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * AutoGen is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ static sigjmp_buf printJumpEnv; static int printJumpSignal = 0; static void printFault( int sig ); static ssize_t safePrintf( char** pzBuf, char* pzFmt, void** argV ); /* = = = START-STATIC-FORWARD = = = */ /* static forward declarations maintained by :mkfwd */ static void printFault( int sig ); static ssize_t safePrintf( char** ppzBuf, char* pzFmt, void** argV ); /* = = = END-STATIC-FORWARD = = = */ static void printFault( int sig ) { printJumpSignal = sig; siglongjmp( printJumpEnv, sig ); } static ssize_t safePrintf( char** ppzBuf, char* pzFmt, void** argV ) { #if ! defined(DEBUG_ENABLED) /* * In normal operation (or during AutoGen testing), seg faults during the * printf operation are caused by bad input data. During AutoGen * development, we do not supply bad printf arguments, so we want to * capture any segfaults when they happen with the correct stack trace. * Therefore, during AutoGen development, we do not protect against seg * faults. */ struct sigaction saSave1; struct sigaction saSave2; { struct sigaction sa; sa.sa_handler = printFault; sa.sa_flags = 0; sigemptyset( &sa.sa_mask ); sigaction( SIGBUS, &sa, &saSave1 ); sigaction( SIGSEGV, &sa, &saSave2 ); } { tSCC zBadArgs[] = "Bad args to sprintf"; tSCC zBadFmt[] = "%s ERROR: %s processing printf format:\n\t%s\n"; /* * IF the sprintfv call below is going to address fault, * THEN ... */ if (sigsetjmp( printJumpEnv, 0 ) != 0) { #ifndef HAVE_STRSIGNAL extern char* strsignal(int signo); #endif /* * IF the fprintf command in the then clause has not failed yet, * THEN perform that fprintf */ if (sigsetjmp( printJumpEnv, 0 ) == 0) fprintf(pfTrace, zBadFmt, pzProg, strsignal(printJumpSignal), pzFmt); /* * The "sprintfv" command below faulted, so we exit */ AG_ABEND( zBadArgs ); } } #endif /* ! defined(DEBUG_ENABLED) */ { int printSize = asprintfv( ppzBuf, pzFmt, (snv_constpointer*)argV ); if ((printSize & ~0xFFFFFU) != 0) /* 1MB max */ AG_ABEND( aprf( "asprintfv returned 0x%08X\n", printSize )); #if ! defined(DEBUG_ENABLED) sigaction( SIGBUS, &saSave1, NULL ); sigaction( SIGSEGV, &saSave2, NULL ); #endif return printSize; } } LOCAL SCM run_printf( char* pzFmt, int len, SCM alist ) { SCM res; void* args[8]; void** arglist; void** argp; if (len < 8) arglist = argp = args; else arglist = argp = (void**)malloc( (len+1) * sizeof(void*)); while (len-- > 0) { SCM car = SCM_CAR( alist ); alist = SCM_CDR( alist ); switch (gh_type_e( car )) { default: case GH_TYPE_UNDEFINED: *(argp++) = (char*)"???"; break; case GH_TYPE_BOOLEAN: *(argp++) = (void*)((car == SCM_BOOL_F) ? "#f" : "#t"); break; case GH_TYPE_CHAR: *(char*)(argp++) = gh_scm2char( car ); break; case GH_TYPE_PAIR: *(argp++) = (char*)".."; break; case GH_TYPE_NUMBER: *(unsigned long*)(argp++) = gh_scm2ulong( car ); break; case GH_TYPE_SYMBOL: case GH_TYPE_STRING: *(argp++) = ag_scm2zchars( car, "printf str" ); break; case GH_TYPE_PROCEDURE: *(argp++) = (char*)"(*)()"; break; case GH_TYPE_VECTOR: case GH_TYPE_LIST: *(argp++) = (char*)"..."; break; } } /* * Do the formatting and allocate a new SCM to hold the result. * Free up any allocations made by ``gh_scm2newstr'' */ { char* pzBuf; size_t bfSize = safePrintf( &pzBuf, pzFmt, arglist ); res = AG_SCM_STR2SCM( pzBuf, bfSize ); free( pzBuf ); } if (arglist != args) AGFREE( (void*)arglist ); return res; } /*=gfunc sprintf * * what: format a string * general_use: * * exparg: format, formatting string * exparg: format-arg, list of arguments to formatting string, opt, list * * doc: Format a string using arguments from the alist. =*/ SCM ag_scm_sprintf( SCM fmt, SCM alist ) { int list_len = scm_ilength( alist ); char* pzFmt = ag_scm2zchars( fmt, zFormat ); if (list_len <= 0) return fmt; return run_printf( pzFmt, list_len, alist ); } /*=gfunc printf * * what: format to stdout * general_use: * * exparg: format, formatting string * exparg: format-arg, list of arguments to formatting string, opt, list * * doc: Format a string using arguments from the alist. * Write to the standard out port. The result will NOT appear in your * output. Use this to print information messages to a template user. * Use ``(sprintf ...)'' to add text to your document. =*/ SCM ag_scm_printf( SCM fmt, SCM alist ) { int list_len = scm_ilength( alist ); char* pzFmt = ag_scm2zchars( fmt, zFormat ); gh_display( run_printf( pzFmt, list_len, alist )); return SCM_UNDEFINED; } /*=gfunc fprintf * * what: format to a file * general_use: * * exparg: port, Guile-scheme output port * exparg: format, formatting string * exparg: format-arg, list of arguments to formatting string, opt, list * * doc: Format a string using arguments from the alist. * Write to a specified port. The result will NOT appear in your * output. Use this to print information messages to a template user. =*/ SCM ag_scm_fprintf( SCM port, SCM fmt, SCM alist ) { int list_len = scm_ilength( alist ); char* pzFmt = ag_scm2zchars( fmt, zFormat ); SCM res = run_printf( pzFmt, list_len, alist ); return scm_display( res, port ); } /*=gfunc hide_email * * what: convert eaddr to javascript * general_use: * * exparg: display, display text * exparg: eaddr, email address * * doc: Hides an email address as a java scriptlett. * The 'mailto:' tag and the email address are coded bytes * rather than plain text. They are also broken up. =*/ SCM ag_scm_hide_email( SCM display, SCM eaddr ) { tSCC zStrt[] = ""; tSCC zFmt[] = "&#%d;"; char* pzDisp = ag_scm2zchars( display, zFormat ); char* pzEadr = ag_scm2zchars( eaddr, zFormat ); size_t str_size = (strlen( pzEadr ) * sizeof( zFmt )) + sizeof( zStrt ) + sizeof( zEnd ) + strlen( pzDisp ); char* pzRes = ag_scribble( str_size ); char* pzScan = pzRes; strcpy( pzScan, zStrt ); pzScan += sizeof( zStrt ) - 1; for (;;) { if (*pzEadr == NUL) break; pzScan += sprintf( pzScan, zFmt, *(pzEadr++) ); } pzScan += sprintf( pzScan, zEnd, pzDisp ); return AG_SCM_STR2SCM( pzRes, (size_t)(pzScan - pzRes)); } /*=gfunc format_arg_count * * what: count the args to a format * general_use: * * exparg: format, formatting string * * doc: "Sometimes, it is useful to simply be able to figure out how many\n" * "arguments are required by a format string. For example, if you\n" * "are extracting a format string for the purpose of generating a\n" * "macro to invoke a printf-like function, you can run the\n" * "formatting string through this function to determine how many\n" * "arguments to provide for in the macro. e.g. for this extraction\n" * "text:\n" * "@example\n\n" * " /" "*=fumble bumble\n" * " * fmt: 'stumble %s: %d\\n'\n" * " =*" "/\n" * "@end example\n\n" * "@noindent\n" * "You may wish to generate a macro:\n" * "@example\n\n" * " #define BUMBLE(a1,a2) printf_like(something,(a1),(a2))\n" * "@end example\n\n" * "@noindent\n" * "You can do this by knowing that the format needs two arguments.\n" =*/ SCM ag_scm_format_arg_count( SCM fmt ) { char* pzFmt = ag_scm2zchars( fmt, zFormat ); int ct = 0; for (;;) { switch (*(pzFmt++)) { case NUL: goto scanDone; case '%': if (*pzFmt == '%') pzFmt++; else ct++; } } scanDone:; return AG_SCM_INT2SCM( ct ); } /* * Local Variables: * mode: C * c-file-style: "stroustrup" * indent-tabs-mode: nil * End: * end of agen5/expPrint.c */