/** * @file expPrint.c * * 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 :-) * * @addtogroup autogen * @{ */ /* * This file is part of AutoGen. * AutoGen Copyright (C) 1992-2016 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 . */ #ifndef DEBUG_ENABLED static sigjmp_buf printJumpEnv; static int printJumpSignal = 0; #endif /* = = = START-STATIC-FORWARD = = = */ static ssize_t safePrintf(char ** ppzBuf, char const * pzFmt, void ** argV); /* = = = END-STATIC-FORWARD = = = */ #ifndef DEBUG_ENABLED static void printFault(int sig) { printJumpSignal = sig; siglongjmp(printJumpEnv, sig); } #endif static ssize_t safePrintf(char ** ppzBuf, char const * 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); } /* * 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(trace_fp, SAFE_PRINTF_BAD_FMT, ag_pname, strsignal(printJumpSignal), pzFmt); /* * The "sprintfv" command below faulted, so we exit */ AG_ABEND(SAFE_PRINTF_BAD_ARGS); } #endif /* ! defined(DEBUG_ENABLED) */ { int p_sz = asprintfv(ppzBuf, pzFmt, (snv_constpointer *)argV); if (((unsigned)p_sz & ~0xFFFFFU) != 0) /* 1MB max */ AG_ABEND(aprf(ASPRINTFV_FAIL_FMT, p_sz)); #if ! defined(DEBUG_ENABLED) sigaction(SIGBUS, &saSave1, NULL); sigaction(SIGSEGV, &saSave2, NULL); #endif return p_sz; } } LOCAL SCM run_printf(char const * pzFmt, int len, SCM alist) { SCM res; void * args[8]; void ** arglist; void ** argp; if (len < 8) arglist = argp = args; else { size_t sz = (size_t)((unsigned)(len+1) * sizeof(void *)); arglist = argp = malloc(sz); } while (len-- > 0) { SCM car = SCM_CAR(alist); alist = SCM_CDR(alist); switch (ag_scm_type_e(car)) { default: case GH_TYPE_UNDEFINED: *(argp++) = (char *)RUN_PRINTF_HUH; break; case GH_TYPE_BOOLEAN: *(argp++) = VOIDP((car == SCM_BOOL_F) ? SCM_FALSE_STR : SCM_TRUE_STR); break; case GH_TYPE_CHAR: *(char *)(argp++) = (char)SCM_CHAR(car); break; case GH_TYPE_PAIR: *(argp++) = (char *)(SCM_LIST_STR+1); break; case GH_TYPE_NUMBER: *(unsigned long *)(argp++) = AG_SCM_TO_ULONG(car); break; case GH_TYPE_SYMBOL: case GH_TYPE_STRING: *(argp++) = ag_scm2zchars(car, "printf str"); break; case GH_TYPE_PROCEDURE: *(argp++) = (char *)SCM_PROC_CAST; break; case GH_TYPE_VECTOR: case GH_TYPE_LIST: *(argp++) = (char *)SCM_LIST_STR; break; } } /* * Do the formatting and allocate a new SCM to hold the result. * Free up any allocations made by ``gh_scm2newstr'' */ { char * bf; size_t bf_sz = (size_t)safePrintf(&bf, pzFmt, arglist); res = scm_from_latin1_stringn(bf, bf_sz); free(bf); } if (arglist != args) AGFREE(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 = (int)scm_ilength(alist); char * pzFmt = ag_scm2zchars(fmt, WORD_FORMAT); 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 = (int)scm_ilength(alist); char * pzFmt = ag_scm2zchars(fmt, WORD_FORMAT); AG_SCM_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 = (int)scm_ilength(alist); char * pzFmt = ag_scm2zchars(fmt, WORD_FORMAT); 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) { char * disp = ag_scm2zchars(display, "fmt"); char * end_adr = ag_scm2zchars(eaddr, "eaddr"); ssize_t str_size = (ssize_t)( (strlen(end_adr) * 6) // e.g. "k" + HIDE_EMAIL_START_STR_LEN + HIDE_EMAIL_END_FMT_LEN + strlen(disp)); char * res = scribble_get(str_size); char * scan = res; char * end = res + (str_size - 1); memcpy(scan, HIDE_EMAIL_START_STR, HIDE_EMAIL_START_STR_LEN); scan += HIDE_EMAIL_START_STR_LEN; do { if (*end_adr == NUL) break; scan += snprintf(scan, str_size - (scan - res), HTML_DEC_DIGIT, *(end_adr++)); } while (scan < end); scan += snprintf(scan, str_size - (scan - res), HIDE_EMAIL_END_FMT, disp); if (scan > end) AG_ABEND(BOGUS_TAG); return scm_from_latin1_stringn(res, (size_t)(scan - res)); } /*=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, WORD_FORMAT); int ct = 0; for (;;) { switch (*(pzFmt++)) { case NUL: goto scanDone; case '%': if (*pzFmt == '%') pzFmt++; else ct++; } } scanDone:; return scm_from_int(ct); } /** * @} * * Local Variables: * mode: C * c-file-style: "stroustrup" * indent-tabs-mode: nil * End: * end of agen5/expPrint.c */