From cd1824e11915d3e5d083d22539395cf71064c711 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 10 Dec 2009 20:51:13 +0000 Subject: [PATCH] Adding switch_mprintf (broken out from sqlite) Adding new %w to mprintf to auto escape both single quote and backslashes Improve the tab completion a tad and fix some sqlite/odbc compat with new mprintf opts Change the default stream writer to use switch_vmprintf so %q/%w can be used there too git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@15875 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- Makefile.am | 2 + src/include/switch.h | 2 +- src/include/switch_core_db.h | 6 - src/include/switch_mprintf.h | 63 ++ src/mod/endpoints/mod_sofia/mod_sofia.c | 14 + src/mod/endpoints/mod_sofia/sofia.c | 9 +- src/mod/endpoints/mod_sofia/sofia_glue.c | 9 +- src/switch_console.c | 115 ++- src/switch_core_db.c | 16 - src/switch_mprintf.c | 867 +++++++++++++++++++++++ 10 files changed, 1059 insertions(+), 44 deletions(-) create mode 100644 src/include/switch_mprintf.h create mode 100644 src/switch_mprintf.c diff --git a/Makefile.am b/Makefile.am index e4a2fc69c9..f8a54d2c9f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -67,6 +67,7 @@ src/switch_buffer.c \ src/switch_caller.c \ src/switch_channel.c \ src/switch_console.c \ +src/switch_mprintf.c\ src/switch_core_media_bug.c \ src/switch_core_timer.c \ src/switch_core_asr.c \ @@ -142,6 +143,7 @@ src/include/switch_core_event_hook.h\ src/include/switch_scheduler.h\ src/include/switch_core.h\ src/include/switch_core_db.h\ +src/include/switch_mprintf.h\ src/include/switch_config.h\ src/include/switch_event.h\ src/include/switch_frame.h\ diff --git a/src/include/switch.h b/src/include/switch.h index 9e608bdae7..c87155cb12 100644 --- a/src/include/switch.h +++ b/src/include/switch.h @@ -106,7 +106,7 @@ #include "switch_platform.h" #include "switch_types.h" #include "switch_apr.h" - +#include "switch_mprintf.h" #include "switch_core_db.h" #include "switch_dso.h" #include "switch_regex.h" diff --git a/src/include/switch_core_db.h b/src/include/switch_core_db.h index 9535113951..86403a556d 100644 --- a/src/include/switch_core_db.h +++ b/src/include/switch_core_db.h @@ -539,12 +539,6 @@ SWITCH_DECLARE(int) switch_core_db_changes(switch_core_db_t *db); * should always use %q instead of %s when inserting text into a string * literal. */ -SWITCH_DECLARE(char *) switch_mprintf(const char *zFormat, ...); - -/*! - * \see switch_mprintf - */ -SWITCH_DECLARE(char *) switch_vmprintf(const char *zFormat, va_list ap); SWITCH_END_EXTERN_C #endif diff --git a/src/include/switch_mprintf.h b/src/include/switch_mprintf.h new file mode 100644 index 0000000000..c0bd4f0f1c --- /dev/null +++ b/src/include/switch_mprintf.h @@ -0,0 +1,63 @@ +/* +** 2001 September 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +*/ +#ifndef SWITCH_MPRINTF_H +#define SWITCH_MPRINTF_H + +SWITCH_BEGIN_EXTERN_C + +/** + * This routine is a variant of the "sprintf()" from the + * standard C library. The resulting string is written into memory + * obtained from malloc() so that there is never a possiblity of buffer + * overflow. This routine also implement some additional formatting + * options that are useful for constructing SQL statements. + * + * The strings returned by this routine should be freed by calling + * switch_core_db_free(). + * + * All of the usual printf formatting options apply. In addition, there + * is a "%q" option. %q works like %s in that it substitutes a null-terminated + * string from the argument list. But %q also doubles every '\'' character. + * %q is designed for use inside a string literal. By doubling each '\'' + * character it escapes that character and allows it to be inserted into + * the string. + * + * For example, so some string variable contains text as follows: + * + * char *zText = "It's a happy day!"; + * + * We can use this text in an SQL statement as follows: + * + * char *z = switch_core_db_mprintf("INSERT INTO TABLES('%q')", zText); + * switch_core_db_exec(db, z, callback1, 0, 0); + * switch_core_db_free(z); + * + * Because the %q format string is used, the '\'' character in zText + * is escaped and the SQL generated is as follows: + * + * INSERT INTO table1 VALUES('It''s a happy day!') + * + * This is correct. Had we used %s instead of %q, the generated SQL + * would have looked like this: + * + * INSERT INTO table1 VALUES('It's a happy day!'); + * + * This second example is an SQL syntax error. As a general rule you + * should always use %q instead of %s when inserting text into a string + * literal. + */ +SWITCH_DECLARE(char *) switch_mprintf(const char *zFormat, ...); +SWITCH_DECLARE(char *) switch_vmprintf(const char *zFormat, va_list ap); + +SWITCH_END_EXTERN_C +#endif /* SWITCH_MPRINTF_H */ diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 70176dadef..42c5d31130 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -3744,6 +3744,18 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_sofia_load) switch_console_set_complete("add sofia profile"); switch_console_set_complete("add sofia profile restart all"); + switch_console_set_complete("add sofia profile _any_ start reloadxml"); + switch_console_set_complete("add sofia profile _any_ stop reloadxml"); + switch_console_set_complete("add sofia profile _any_ rescan reloadxml"); + switch_console_set_complete("add sofia profile _any_ restart reloadxml"); + + switch_console_set_complete("add sofia profile _any_ flush_inbound_reg"); + switch_console_set_complete("add sofia profile _any_ register"); + switch_console_set_complete("add sofia profile _any_ killgw"); + switch_console_set_complete("add sofia profile _any_ siptrace on"); + switch_console_set_complete("add sofia profile _any_ siptrace off"); + + SWITCH_ADD_API(api_interface, "sofia_contact", "Sofia Contacts", sofia_contact_function, "[profile/]@"); SWITCH_ADD_CHAT(chat_interface, SOFIA_CHAT_PROTO, sofia_presence_chat_send); @@ -3755,6 +3767,8 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_sofia_shutdown) { int sanity = 0; + switch_console_set_complete("del sofia"); + switch_mutex_lock(mod_sofia_globals.mutex); if (mod_sofia_globals.running == 1) { mod_sofia_globals.running = 0; diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index bf688071e6..9e5fc7d074 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -1059,7 +1059,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void int sanity; switch_thread_t *worker_thread; switch_status_t st; - + char cbuf[512] = ""; switch_mutex_lock(mod_sofia_globals.mutex); @@ -1226,10 +1226,17 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void switch_yield(1000000); + switch_snprintf(cbuf, sizeof(cbuf), "add sofia profile %s", profile->name); + switch_console_set_complete(cbuf); + + while (mod_sofia_globals.running == 1 && sofia_test_pflag(profile, PFLAG_RUNNING) && sofia_test_pflag(profile, PFLAG_WORKER_RUNNING)) { su_root_step(profile->s_root, 1000); } + switch_snprintf(cbuf, sizeof(cbuf), "del sofia profile %s", profile->name); + switch_console_set_complete(cbuf); + sofia_clear_pflag_locked(profile, PFLAG_RUNNING); switch_core_session_hupall_matching_var("sofia_profile_name", profile->name, SWITCH_CAUSE_MANAGER_REQUEST); diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index db07c3f8e9..7e4df58ce6 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -3644,10 +3644,12 @@ void sofia_glue_release_profile__(const char *file, const char *func, int line, switch_status_t sofia_glue_add_profile(char *key, sofia_profile_t *profile) { switch_status_t status = SWITCH_STATUS_FALSE; - + char cbuf[512] = ""; switch_mutex_lock(mod_sofia_globals.hash_mutex); if (!switch_core_hash_find(mod_sofia_globals.profile_hash, key)) { status = switch_core_hash_insert(mod_sofia_globals.profile_hash, key, profile); + switch_snprintf(cbuf, sizeof(cbuf), "add sofia profile %s", key); + switch_console_set_complete(cbuf); } switch_mutex_unlock(mod_sofia_globals.hash_mutex); @@ -3716,13 +3718,16 @@ void sofia_glue_del_profile(sofia_profile_t *profile) const void *var; void *val; sofia_profile_t *pptr; - + char cbuf[512] = ""; + switch_mutex_lock(mod_sofia_globals.hash_mutex); if (mod_sofia_globals.profile_hash) { for (hi = switch_hash_first(NULL, mod_sofia_globals.profile_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, &var, NULL, &val); if ((pptr = (sofia_profile_t *) val) && pptr == profile) { aliases[i++] = strdup((char *) var); + switch_snprintf(cbuf, sizeof(cbuf), "del sofia profile %s", var); + switch_console_set_complete(cbuf); if (i == 512) { abort(); } diff --git a/src/switch_console.c b/src/switch_console.c index 431f064b2d..fb6facdfb2 100644 --- a/src/switch_console.c +++ b/src/switch_console.c @@ -122,7 +122,10 @@ SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_stream_write(switch_stream } va_start(ap, fmt); - ret = switch_vasprintf(&data, fmt, ap); + //ret = switch_vasprintf(&data, fmt, ap); + if (!(data = switch_vmprintf(fmt, ap))) { + ret = -1; + } va_end(ap); if (data) { @@ -205,7 +208,12 @@ char *expand_alias(char *cmd, char *arg) switch_core_db_handle(&db); - sql = switch_mprintf("select command from aliases where alias='%q'", cmd); + + if (db->type == SCDB_TYPE_CORE_DB) { + sql = switch_mprintf("select command from aliases where alias='%q'", cmd); + } else { + sql = switch_mprintf("select command from aliases where alias='%w'", cmd); + } switch_cache_db_execute_sql_callback(db, sql, alias_callback, &r, &errmsg); @@ -217,7 +225,11 @@ char *expand_alias(char *cmd, char *arg) switch_safe_free(sql); if (!r) { - sql = switch_mprintf("select command from aliases where alias='%q %q'", cmd, arg); + if (db->type == SCDB_TYPE_CORE_DB) { + sql = switch_mprintf("select command from aliases where alias='%q %q'", cmd, arg); + } else { + sql = switch_mprintf("select command from aliases where alias='%w %w'", cmd, arg); + } switch_cache_db_execute_sql_callback(db, sql, alias_callback, &r, &errmsg); @@ -499,6 +511,7 @@ struct helper { int hits; int words; char last[512]; + char partial[512]; FILE *out; }; @@ -506,19 +519,37 @@ static int comp_callback(void *pArg, int argc, char **argv, char **columnNames) { struct helper *h = (struct helper *) pArg; char *target = NULL; - + switch_size_t x, y; + int i; + target = argv[0]; if (!target) { return -1; } - fprintf(h->out, "[%20s]\t", target); + if (!zstr(target)) { + fprintf(h->out, "[%20s]\t", target); + switch_copy_string(h->last, target, sizeof(h->last)); + h->hits++; + } + + x = strlen(h->last); + y = strlen(h->partial); - switch_copy_string(h->last, target, sizeof(h->last)); + if (h->hits > 1) { + for(i = 0; i < x && i < y; i++) { + if (h->last[i] != h->partial[i]) { + h->partial[i] = '\0'; + break; + } + } + } else if (h->hits == 1) { + switch_copy_string(h->partial, target, sizeof(h->last)); + } if (!zstr(target)) { - if ((++h->hits % 4) == 0) { + if ((h->hits % 4) == 0) { fprintf(h->out, "\n"); } } @@ -600,8 +631,20 @@ static unsigned char complete(EditLine * el, int ch) } - for (x = 0; x < argc; x++) { - stream.write_function(&stream, "(a%d = '' or a%d like '%s%%')%s", x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and "); + for (x = 0; x < argc && x < 11; x++) { + if (h.words + 1 > argc) { + if (db->type == SCDB_TYPE_CORE_DB) { + stream.write_function(&stream, "(a%d = '' or a%d = '%q')%q", x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and "); + } else { + stream.write_function(&stream, "(a%d = '' or a%d = '%w')%w", x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and "); + } + } else { + if (db->type == SCDB_TYPE_CORE_DB) { + stream.write_function(&stream, "(a%d = '' or a%d like '%q%%')%q", x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and "); + } else { + stream.write_function(&stream, "(a%d = '' or a%d like '%w%%')%w", x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and "); + } + } } stream.write_function(&stream, " and hostname='%s'", switch_core_get_variable("hostname")); @@ -624,9 +667,13 @@ static unsigned char complete(EditLine * el, int ch) fprintf(h.out, "\n\n"); - if (h.hits == 1) { + if (h.hits == 1 && !zstr(h.last)) { el_deletestr(el, h.len); el_insertstr(el, h.last); + el_insertstr(el, " "); + } else if (h.hits > 1 && !zstr(h.partial)) { + el_deletestr(el, h.len); + el_insertstr(el, h.partial); } end: @@ -655,11 +702,19 @@ SWITCH_DECLARE(switch_status_t) switch_console_set_complete(const char *string) SWITCH_STANDARD_STREAM(mystream); switch_core_db_handle(&db); - + if (!strcasecmp(argv[0], "stickyadd")) { mystream.write_function(&mystream, "insert into complete values (1,"); for (x = 0; x < 10; x++) { - mystream.write_function(&mystream, "'%s', ", switch_str_nil(argv[x + 1])); + if (argv[x + 1] && !strcasecmp(argv[x + 1], "_any_")) { + mystream.write_function(&mystream, "%s", "'', "); + } else { + if (db->type == SCDB_TYPE_CORE_DB) { + mystream.write_function(&mystream, "'%q', ", switch_str_nil(argv[x + 1])); + } else { + mystream.write_function(&mystream, "'%w', ", switch_str_nil(argv[x + 1])); + } + } } mystream.write_function(&mystream, " '%s')", switch_core_get_variable("hostname")); switch_cache_db_persistant_execute(db, mystream.data, 5); @@ -667,9 +722,18 @@ SWITCH_DECLARE(switch_status_t) switch_console_set_complete(const char *string) } else if (!strcasecmp(argv[0], "add")) { mystream.write_function(&mystream, "insert into complete values (0,"); for (x = 0; x < 10; x++) { - mystream.write_function(&mystream, "'%s', ", switch_str_nil(argv[x + 1])); + if (argv[x + 1] && !strcasecmp(argv[x + 1], "_any_")) { + mystream.write_function(&mystream, "%s", "'', "); + } else { + if (db->type == SCDB_TYPE_CORE_DB) { + mystream.write_function(&mystream, "'%q', ", switch_str_nil(argv[x + 1])); + } else { + mystream.write_function(&mystream, "'%w', ", switch_str_nil(argv[x + 1])); + } + } } mystream.write_function(&mystream, " '%s')", switch_core_get_variable("hostname")); + switch_cache_db_persistant_execute(db, mystream.data, 5); status = SWITCH_STATUS_SUCCESS; } else if (!strcasecmp(argv[0], "del")) { @@ -679,13 +743,18 @@ SWITCH_DECLARE(switch_status_t) switch_console_set_complete(const char *string) } else { mystream.write_function(&mystream, "delete from complete where "); for (x = 0; x < argc - 1; x++) { - mystream.write_function(&mystream, "a%d = '%s'%s", x + 1, switch_str_nil(argv[x + 1]), x == argc - 2 ? "" : " and "); + if (db->type == SCDB_TYPE_CORE_DB) { + mystream.write_function(&mystream, "a%d = '%q'%q", x + 1, switch_str_nil(argv[x + 1]), x == argc - 2 ? "" : " and "); + } else { + mystream.write_function(&mystream, "a%d = '%w'%w", x + 1, switch_str_nil(argv[x + 1]), x == argc - 2 ? "" : " and "); + } } mystream.write_function(&mystream, " and hostname='%s'", switch_core_get_variable("hostname")); switch_cache_db_persistant_execute(db, mystream.data, 1); } status = SWITCH_STATUS_SUCCESS; } + switch_safe_free(mystream.data); switch_cache_db_release_db_handle(&db); } @@ -715,16 +784,26 @@ SWITCH_DECLARE(switch_status_t) switch_console_set_alias(const char *string) sql = switch_mprintf("delete from aliases where alias='%q' and hostname='%q'", argv[1], switch_core_get_variable("hostname")); switch_cache_db_persistant_execute(db, sql, 5); switch_safe_free(sql); - sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (1, '%q','%q','%q')", - argv[1], argv[2], switch_core_get_variable("hostname")); + if (db->type == SCDB_TYPE_CORE_DB) { + sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (1, '%q','%q','%q')", + argv[1], argv[2], switch_core_get_variable("hostname")); + } else { + sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (1, '%w','%w','%w')", + argv[1], argv[2], switch_core_get_variable("hostname")); + } switch_cache_db_persistant_execute(db, sql, 5); status = SWITCH_STATUS_SUCCESS; } else if (!strcasecmp(argv[0], "add") && argc == 3) { sql = switch_mprintf("delete from aliases where alias='%q' and hostname='%q'", argv[1], switch_core_get_variable("hostname")); switch_cache_db_persistant_execute(db, sql, 5); switch_safe_free(sql); - sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (0, '%q','%q','%q')", - argv[1], argv[2], switch_core_get_variable("hostname")); + if (db->type == SCDB_TYPE_CORE_DB) { + sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (0, '%q','%q','%q')", + argv[1], argv[2], switch_core_get_variable("hostname")); + } else { + sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (0, '%w','%w','%w')", + argv[1], argv[2], switch_core_get_variable("hostname")); + } switch_cache_db_persistant_execute(db, sql, 5); status = SWITCH_STATUS_SUCCESS; } else if (!strcasecmp(argv[0], "del") && argc == 2) { diff --git a/src/switch_core_db.c b/src/switch_core_db.c index 893626b8e1..17cc01f060 100644 --- a/src/switch_core_db.c +++ b/src/switch_core_db.c @@ -179,22 +179,6 @@ SWITCH_DECLARE(int) switch_core_db_changes(switch_core_db_t *db) return sqlite3_changes(db); } -SWITCH_DECLARE(char *) switch_mprintf(const char *zFormat, ...) -{ - va_list ap; - char *z; - va_start(ap, zFormat); - z = sqlite3_vmprintf(zFormat, ap); - va_end(ap); - return z; -} - -SWITCH_DECLARE(char *) switch_vmprintf(const char *zFormat, va_list ap) -{ - - return sqlite3_vmprintf(zFormat, ap); -} - SWITCH_DECLARE(switch_core_db_t *) switch_core_db_open_file(const char *filename) { switch_core_db_t *db; diff --git a/src/switch_mprintf.c b/src/switch_mprintf.c new file mode 100644 index 0000000000..64d83652b3 --- /dev/null +++ b/src/switch_mprintf.c @@ -0,0 +1,867 @@ +/* +** The "printf" code that follows dates from the 1980's. It is in +** the public domain. The original comments are included here for +** completeness. They are very out-of-date but might be useful as +** an historical reference. Most of the "enhancements" have been backed +** out so that the functionality is now the same as standard printf(). +** +************************************************************************** +** +** The following modules is an enhanced replacement for the "printf" subroutines +** found in the standard C library. The following enhancements are +** supported: +** +** + Additional functions. The standard set of "printf" functions +** includes printf, fprintf, sprintf, vprintf, vfprintf, and +** vsprintf. This module adds the following: +** +** * snprintf -- Works like sprintf, but has an extra argument +** which is the size of the buffer written to. +** +** * mprintf -- Similar to sprintf. Writes output to memory +** obtained from malloc. +** +** * xprintf -- Calls a function to dispose of output. +** +** * nprintf -- No output, but returns the number of characters +** that would have been output by printf. +** +** * A v- version (ex: vsnprintf) of every function is also +** supplied. +** +** + A few extensions to the formatting notation are supported: +** +** * The "=" flag (similar to "-") causes the output to be +** be centered in the appropriately sized field. +** +** * The %b field outputs an integer in binary notation. +** +** * The %c field now accepts a precision. The character output +** is repeated by the number of times the precision specifies. +** +** * The %' field works like %c, but takes as its character the +** next character of the format string, instead of the next +** argument. For example, printf("%.78'-") prints 78 minus +** signs, the same as printf("%.78c",'-'). +** +** + When compiled using GCC on a SPARC, this version of printf is +** faster than the library printf for SUN OS 4.1. +** +** + All functions are fully reentrant. +** +*/ +/* + * 20090210 (stkn): + * Taken from sqlite-3.3.x, + * renamed SQLITE_ -> SWITCH_, + * renamed visible functions to switch_* + * disabled functions without extra conversion specifiers + */ + +#include + +#define LONGDOUBLE_TYPE long double + +/* +** Conversion types fall into various categories as defined by the +** following enumeration. +*/ +#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */ +#define etFLOAT 2 /* Floating point. %f */ +#define etEXP 3 /* Exponentional notation. %e and %E */ +#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */ +#define etSIZE 5 /* Return number of characters processed so far. %n */ +#define etSTRING 6 /* Strings. %s */ +#define etDYNSTRING 7 /* Dynamically allocated strings. %z */ +#define etPERCENT 8 /* Percent symbol. %% */ +#define etCHARX 9 /* Characters. %c */ +/* The rest are extensions, not normally found in printf() */ +#define etCHARLIT 10 /* Literal characters. %' */ +#define etSQLESCAPE 11 /* Strings with '\'' doubled. %q */ +#define etSQLESCAPE2 12 /* Strings with '\'' doubled and enclosed in '', + NULL pointers replaced by SQL NULL. %Q */ +#ifdef __UNSUPPORTED__ +#define etTOKEN 13 /* a pointer to a Token structure */ +#define etSRCLIST 14 /* a pointer to a SrcList */ +#endif +#define etPOINTER 15 /* The %p conversion */ +#define etSQLESCAPE3 16 + +/* +** An "etByte" is an 8-bit unsigned value. +*/ +typedef unsigned char etByte; + +/* +** Each builtin conversion character (ex: the 'd' in "%d") is described +** by an instance of the following structure +*/ +typedef struct et_info { /* Information about each format field */ + char fmttype; /* The format field code letter */ + etByte base; /* The base for radix conversion */ + etByte flags; /* One or more of FLAG_ constants below */ + etByte type; /* Conversion paradigm */ + etByte charset; /* Offset into aDigits[] of the digits string */ + etByte prefix; /* Offset into aPrefix[] of the prefix string */ +} et_info; + +/* +** Allowed values for et_info.flags +*/ +#define FLAG_SIGNED 1 /* True if the value to convert is signed */ +#define FLAG_INTERN 2 /* True if for internal use only */ +#define FLAG_STRING 4 /* Allow infinity precision */ + + +/* +** The following table is searched linearly, so it is good to put the +** most frequently used conversion types first. +*/ +static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; +static const char aPrefix[] = "-x0\000X0"; +static const et_info fmtinfo[] = { + { 'd', 10, 1, etRADIX, 0, 0 }, + { 's', 0, 4, etSTRING, 0, 0 }, + { 'g', 0, 1, etGENERIC, 30, 0 }, + { 'z', 0, 6, etDYNSTRING, 0, 0 }, + { 'q', 0, 4, etSQLESCAPE, 0, 0 }, + { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, + { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, + { 'c', 0, 0, etCHARX, 0, 0 }, + { 'o', 8, 0, etRADIX, 0, 2 }, + { 'u', 10, 0, etRADIX, 0, 0 }, + { 'x', 16, 0, etRADIX, 16, 1 }, + { 'X', 16, 0, etRADIX, 0, 4 }, +#ifndef SWITCH_OMIT_FLOATING_POINT + { 'f', 0, 1, etFLOAT, 0, 0 }, + { 'e', 0, 1, etEXP, 30, 0 }, + { 'E', 0, 1, etEXP, 14, 0 }, + { 'G', 0, 1, etGENERIC, 14, 0 }, +#endif + { 'i', 10, 1, etRADIX, 0, 0 }, + { 'n', 0, 0, etSIZE, 0, 0 }, + { '%', 0, 0, etPERCENT, 0, 0 }, + { 'p', 16, 0, etPOINTER, 0, 1 }, +#ifdef __UNSUPPORTED__ + { 'T', 0, 2, etTOKEN, 0, 0 }, + { 'S', 0, 2, etSRCLIST, 0, 0 }, +#endif +}; +#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0])) + +/* +** If SWITCH_OMIT_FLOATING_POINT is defined, then none of the floating point +** conversions will work. +*/ +#ifndef SWITCH_OMIT_FLOATING_POINT +/* +** "*val" is a double such that 0.1 <= *val < 10.0 +** Return the ascii code for the leading digit of *val, then +** multiply "*val" by 10.0 to renormalize. +** +** Example: +** input: *val = 3.14159 +** output: *val = 1.4159 function return = '3' +** +** The counter *cnt is incremented each time. After counter exceeds +** 16 (the number of significant digits in a 64-bit float) '0' is +** always returned. +*/ +static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ + int digit; + LONGDOUBLE_TYPE d; + if( (*cnt)++ >= 16 ) return '0'; + digit = (int)*val; + d = digit; + digit += '0'; + *val = (*val - d)*10.0; + return digit; +} +#endif /* SWITCH_OMIT_FLOATING_POINT */ + +/* +** On machines with a small stack size, you can redefine the +** SWITCH_PRINT_BUF_SIZE to be less than 350. But beware - for +** smaller values some %f conversions may go into an infinite loop. +*/ +#ifndef SWITCH_PRINT_BUF_SIZE +# define SWITCH_PRINT_BUF_SIZE 350 +#endif +#define etBUFSIZE SWITCH_PRINT_BUF_SIZE /* Size of the output buffer */ + +/* +** The root program. All variations call this core. +** +** INPUTS: +** func This is a pointer to a function taking three arguments +** 1. A pointer to anything. Same as the "arg" parameter. +** 2. A pointer to the list of characters to be output +** (Note, this list is NOT null terminated.) +** 3. An integer number of characters to be output. +** (Note: This number might be zero.) +** +** arg This is the pointer to anything which will be passed as the +** first argument to "func". Use it for whatever you like. +** +** fmt This is the format string, as in the usual print. +** +** ap This is a pointer to a list of arguments. Same as in +** vfprint. +** +** OUTPUTS: +** The return value is the total number of characters sent to +** the function "func". Returns -1 on a error. +** +** Note that the order in which automatic variables are declared below +** seems to make a big difference in determining how fast this beast +** will run. +*/ +static int vxprintf( + void (*func)(void*,const char*,int), /* Consumer of text */ + void *arg, /* First argument to the consumer */ + int useExtended, /* Allow extended %-conversions */ + const char *fmt, /* Format string */ + va_list ap /* arguments */ +){ + int c; /* Next character in the format string */ + char *bufpt; /* Pointer to the conversion buffer */ + int precision; /* Precision of the current field */ + int length; /* Length of the field */ + int idx; /* A general purpose loop counter */ + int count; /* Total number of characters output */ + int width; /* Width of the current field */ + etByte flag_leftjustify; /* True if "-" flag is present */ + etByte flag_plussign; /* True if "+" flag is present */ + etByte flag_blanksign; /* True if " " flag is present */ + etByte flag_alternateform; /* True if "#" flag is present */ + etByte flag_altform2; /* True if "!" flag is present */ + etByte flag_zeropad; /* True if field width constant starts with zero */ + etByte flag_long; /* True if "l" flag is present */ + etByte flag_longlong; /* True if the "ll" flag is present */ + etByte done; /* Loop termination flag */ + uint64_t longvalue; /* Value for integer types */ + LONGDOUBLE_TYPE realvalue; /* Value for real types */ + const et_info *infop; /* Pointer to the appropriate info structure */ + char buf[etBUFSIZE]; /* Conversion buffer */ + char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ + etByte errorflag = 0; /* True if an error is encountered */ + etByte xtype; /* Conversion paradigm */ + char *zExtra; /* Extra memory used for etTCLESCAPE conversions */ + static const char spaces[] = + " "; +#define etSPACESIZE (sizeof(spaces)-1) +#ifndef SWITCH_OMIT_FLOATING_POINT + int exp, e2; /* exponent of real numbers */ + double rounder; /* Used for rounding floating point values */ + etByte flag_dp; /* True if decimal point should be shown */ + etByte flag_rtz; /* True if trailing zeros should be removed */ + etByte flag_exp; /* True to force display of the exponent */ + int nsd; /* Number of significant digits returned */ +#endif + + func(arg,"",0); + count = length = 0; + bufpt = 0; + for(; (c=(*fmt))!=0; ++fmt){ + if( c!='%' ){ + int amt; + bufpt = (char *)fmt; + amt = 1; + while( (c=(*++fmt))!='%' && c!=0 ) amt++; + (*func)(arg,bufpt,amt); + count += amt; + if( c==0 ) break; + } + if( (c=(*++fmt))==0 ){ + errorflag = 1; + (*func)(arg,"%",1); + count++; + break; + } + /* Find out what flags are present */ + flag_leftjustify = flag_plussign = flag_blanksign = + flag_alternateform = flag_altform2 = flag_zeropad = 0; + done = 0; + do{ + switch( c ){ + case '-': flag_leftjustify = 1; break; + case '+': flag_plussign = 1; break; + case ' ': flag_blanksign = 1; break; + case '#': flag_alternateform = 1; break; + case '!': flag_altform2 = 1; break; + case '0': flag_zeropad = 1; break; + default: done = 1; break; + } + }while( !done && (c=(*++fmt))!=0 ); + /* Get the field width */ + width = 0; + if( c=='*' ){ + width = va_arg(ap,int); + if( width<0 ){ + flag_leftjustify = 1; + width = -width; + } + c = *++fmt; + }else{ + while( c>='0' && c<='9' ){ + width = width*10 + c - '0'; + c = *++fmt; + } + } + if( width > etBUFSIZE-10 ){ + width = etBUFSIZE-10; + } + /* Get the precision */ + if( c=='.' ){ + precision = 0; + c = *++fmt; + if( c=='*' ){ + precision = va_arg(ap,int); + if( precision<0 ) precision = -precision; + c = *++fmt; + }else{ + while( c>='0' && c<='9' ){ + precision = precision*10 + c - '0'; + c = *++fmt; + } + } + }else{ + precision = -1; + } + /* Get the conversion type modifier */ + if( c=='l' ){ + flag_long = 1; + c = *++fmt; + if( c=='l' ){ + flag_longlong = 1; + c = *++fmt; + }else{ + flag_longlong = 0; + } + }else{ + flag_long = flag_longlong = 0; + } + /* Fetch the info entry for the field */ + infop = 0; + for(idx=0; idxflags & FLAG_INTERN)==0 ){ + xtype = infop->type; + }else{ + return -1; + } + break; + } + } + zExtra = 0; + if( infop==0 ){ + return -1; + } + + + /* Limit the precision to prevent overflowing buf[] during conversion */ + if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){ + precision = etBUFSIZE-40; + } + + /* + ** At this point, variables are initialized as follows: + ** + ** flag_alternateform TRUE if a '#' is present. + ** flag_altform2 TRUE if a '!' is present. + ** flag_plussign TRUE if a '+' is present. + ** flag_leftjustify TRUE if a '-' is present or if the + ** field width was negative. + ** flag_zeropad TRUE if the width began with 0. + ** flag_long TRUE if the letter 'l' (ell) prefixed + ** the conversion character. + ** flag_longlong TRUE if the letter 'll' (ell ell) prefixed + ** the conversion character. + ** flag_blanksign TRUE if a ' ' is present. + ** width The specified field width. This is + ** always non-negative. Zero is the default. + ** precision The specified precision. The default + ** is -1. + ** xtype The class of the conversion. + ** infop Pointer to the appropriate info struct. + */ + switch( xtype ){ + case etPOINTER: + flag_longlong = sizeof(char*)==sizeof(int64_t); + flag_long = sizeof(char*)==sizeof(long int); + /* Fall through into the next case */ + case etRADIX: + if( infop->flags & FLAG_SIGNED ){ + int64_t v; + if( flag_longlong ) v = va_arg(ap,int64_t); + else if( flag_long ) v = va_arg(ap,long int); + else v = va_arg(ap,int); + if( v<0 ){ + longvalue = -v; + prefix = '-'; + }else{ + longvalue = v; + if( flag_plussign ) prefix = '+'; + else if( flag_blanksign ) prefix = ' '; + else prefix = 0; + } + }else{ + if( flag_longlong ) longvalue = va_arg(ap,uint64_t); + else if( flag_long ) longvalue = va_arg(ap,unsigned long int); + else longvalue = va_arg(ap,unsigned int); + prefix = 0; + } + if( longvalue==0 ) flag_alternateform = 0; + if( flag_zeropad && precisioncharset]; + base = infop->base; + do{ /* Convert to ascii */ + *(--bufpt) = cset[longvalue%base]; + longvalue = longvalue/base; + }while( longvalue>0 ); + } + length = &buf[etBUFSIZE-1]-bufpt; + for(idx=precision-length; idx>0; idx--){ + *(--bufpt) = '0'; /* Zero pad */ + } + if( prefix ) *(--bufpt) = prefix; /* Add sign */ + if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ + const char *pre; + char x; + pre = &aPrefix[infop->prefix]; + if( *bufpt!=pre[0] ){ + for(; (x=(*pre))!=0; pre++) *(--bufpt) = x; + } + } + length = &buf[etBUFSIZE-1]-bufpt; + break; + case etFLOAT: + case etEXP: + case etGENERIC: + realvalue = va_arg(ap,double); +#ifndef SWITCH_OMIT_FLOATING_POINT + if( precision<0 ) precision = 6; /* Set default precision */ + if( precision>etBUFSIZE/2-10 ) precision = etBUFSIZE/2-10; + if( realvalue<0.0 ){ + realvalue = -realvalue; + prefix = '-'; + }else{ + if( flag_plussign ) prefix = '+'; + else if( flag_blanksign ) prefix = ' '; + else prefix = 0; + } + if( xtype==etGENERIC && precision>0 ) precision--; +#if 0 + /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */ + for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); +#else + /* It makes more sense to use 0.5 */ + for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){} +#endif + if( xtype==etFLOAT ) realvalue += rounder; + /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ + exp = 0; + if( realvalue>0.0 ){ + while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; } + while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } + while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } + while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; } + while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; } + if( exp>350 || exp<-350 ){ + bufpt = "NaN"; + length = 3; + break; + } + } + bufpt = buf; + /* + ** If the field type is etGENERIC, then convert to either etEXP + ** or etFLOAT, as appropriate. + */ + flag_exp = xtype==etEXP; + if( xtype!=etFLOAT ){ + realvalue += rounder; + if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } + } + if( xtype==etGENERIC ){ + flag_rtz = !flag_alternateform; + if( exp<-4 || exp>precision ){ + xtype = etEXP; + }else{ + precision = precision - exp; + xtype = etFLOAT; + } + }else{ + flag_rtz = 0; + } + if( xtype==etEXP ){ + e2 = 0; + }else{ + e2 = exp; + } + nsd = 0; + flag_dp = (precision>0) | flag_alternateform | flag_altform2; + /* The sign in front of the number */ + if( prefix ){ + *(bufpt++) = prefix; + } + /* Digits prior to the decimal point */ + if( e2<0 ){ + *(bufpt++) = '0'; + }else{ + for(; e2>=0; e2--){ + *(bufpt++) = et_getdigit(&realvalue,&nsd); + } + } + /* The decimal point */ + if( flag_dp ){ + *(bufpt++) = '.'; + } + /* "0" digits after the decimal point but before the first + ** significant digit of the number */ + for(e2++; e2<0 && precision>0; precision--, e2++){ + *(bufpt++) = '0'; + } + /* Significant digits after the decimal point */ + while( (precision--)>0 ){ + *(bufpt++) = et_getdigit(&realvalue,&nsd); + } + /* Remove trailing zeros and the "." if no digits follow the "." */ + if( flag_rtz && flag_dp ){ + while( bufpt[-1]=='0' ) *(--bufpt) = 0; + assert( bufpt>buf ); + if( bufpt[-1]=='.' ){ + if( flag_altform2 ){ + *(bufpt++) = '0'; + }else{ + *(--bufpt) = 0; + } + } + } + /* Add the "eNNN" suffix */ + if( flag_exp || (xtype==etEXP && exp) ){ + *(bufpt++) = aDigits[infop->charset]; + if( exp<0 ){ + *(bufpt++) = '-'; exp = -exp; + }else{ + *(bufpt++) = '+'; + } + if( exp>=100 ){ + *(bufpt++) = (exp/100)+'0'; /* 100's digit */ + exp %= 100; + } + *(bufpt++) = exp/10+'0'; /* 10's digit */ + *(bufpt++) = exp%10+'0'; /* 1's digit */ + } + *bufpt = 0; + + /* The converted number is in buf[] and zero terminated. Output it. + ** Note that the number is in the usual order, not reversed as with + ** integer conversions. */ + length = bufpt-buf; + bufpt = buf; + + /* Special case: Add leading zeros if the flag_zeropad flag is + ** set and we are not left justified */ + if( flag_zeropad && !flag_leftjustify && length < width){ + int i; + int nPad = width - length; + for(i=width; i>=nPad; i--){ + bufpt[i] = bufpt[i-nPad]; + } + i = prefix!=0; + while( nPad-- ) bufpt[i++] = '0'; + length = width; + } +#endif + break; + case etSIZE: + *(va_arg(ap,int*)) = count; + length = width = 0; + break; + case etPERCENT: + buf[0] = '%'; + bufpt = buf; + length = 1; + break; + case etCHARLIT: + case etCHARX: + c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt); + if( precision>=0 ){ + for(idx=1; idx=0 && precisionetBUFSIZE ){ + bufpt = zExtra = malloc( n ); + if( bufpt==0 ) return -1; + }else{ + bufpt = buf; + } + j = 0; + if( needQuote ) bufpt[j++] = '\''; + for(i=0; (ch=escarg[i])!=0; i++){ + bufpt[j++] = ch; + if( ch=='\'' || ( xtype==etSQLESCAPE3 && ch=='\\')) bufpt[j++] = ch; + } + if( needQuote ) bufpt[j++] = '\''; + bufpt[j] = 0; + length = j; + /* The precision is ignored on %q and %Q */ + /* if( precision>=0 && precisionz ){ + (*func)(arg, (char*)pToken->z, pToken->n); + } + length = width = 0; + break; + } + case etSRCLIST: { + SrcList *pSrc = va_arg(ap, SrcList*); + int k = va_arg(ap, int); + struct SrcList_item *pItem = &pSrc->a[k]; + assert( k>=0 && knSrc ); + if( pItem->zDatabase && pItem->zDatabase[0] ){ + (*func)(arg, pItem->zDatabase, strlen(pItem->zDatabase)); + (*func)(arg, ".", 1); + } + (*func)(arg, pItem->zName, strlen(pItem->zName)); + length = width = 0; + break; + } +#endif + }/* End switch over the format type */ + /* + ** The text of the conversion is pointed to by "bufpt" and is + ** "length" characters long. The field width is "width". Do + ** the output. + */ + if( !flag_leftjustify ){ + register int nspace; + nspace = width-length; + if( nspace>0 ){ + count += nspace; + while( nspace>=etSPACESIZE ){ + (*func)(arg,spaces,etSPACESIZE); + nspace -= etSPACESIZE; + } + if( nspace>0 ) (*func)(arg,spaces,nspace); + } + } + if( length>0 ){ + (*func)(arg,bufpt,length); + count += length; + } + if( flag_leftjustify ){ + register int nspace; + nspace = width-length; + if( nspace>0 ){ + count += nspace; + while( nspace>=etSPACESIZE ){ + (*func)(arg,spaces,etSPACESIZE); + nspace -= etSPACESIZE; + } + if( nspace>0 ) (*func)(arg,spaces,nspace); + } + } + if( zExtra ){ + free(zExtra); + } + }/* End for loop over the format string */ + return errorflag ? -1 : count; +} /* End of function */ + + +/* This structure is used to store state information about the +** write to memory that is currently in progress. +*/ +struct sgMprintf { + char *zBase; /* A base allocation */ + char *zText; /* The string collected so far */ + int nChar; /* Length of the string so far */ + int nTotal; /* Output size if unconstrained */ + int nAlloc; /* Amount of space allocated in zText */ + void *(*xRealloc)(void*,int); /* Function used to realloc memory */ +}; + +/* +** This function implements the callback from vxprintf. +** +** This routine add nNewChar characters of text in zNewText to +** the sgMprintf structure pointed to by "arg". +*/ +static void mout(void *arg, const char *zNewText, int nNewChar){ + struct sgMprintf *pM = (struct sgMprintf*)arg; + pM->nTotal += nNewChar; + if( pM->nChar + nNewChar + 1 > pM->nAlloc ){ + if( pM->xRealloc==0 ){ + nNewChar = pM->nAlloc - pM->nChar - 1; + }else{ + pM->nAlloc = pM->nChar + nNewChar*2 + 1; + if( pM->zText==pM->zBase ){ + pM->zText = pM->xRealloc(0, pM->nAlloc); + if( pM->zText && pM->nChar ){ + memcpy(pM->zText, pM->zBase, pM->nChar); + } + }else{ + char *zNew; + zNew = pM->xRealloc(pM->zText, pM->nAlloc); + if( zNew ){ + pM->zText = zNew; + } + } + } + } + if( pM->zText ){ + if( nNewChar>0 ){ + memcpy(&pM->zText[pM->nChar], zNewText, nNewChar); + pM->nChar += nNewChar; + } + pM->zText[pM->nChar] = 0; + } +} + +/* +** This routine is a wrapper around xprintf() that invokes mout() as +** the consumer. +*/ +static char *base_vprintf( + void *(*xRealloc)(void*,int), /* Routine to realloc memory. May be NULL */ + int useInternal, /* Use internal %-conversions if true */ + char *zInitBuf, /* Initially write here, before mallocing */ + int nInitBuf, /* Size of zInitBuf[] */ + const char *zFormat, /* format string */ + va_list ap /* arguments */ +){ + struct sgMprintf sM; + sM.zBase = sM.zText = zInitBuf; + sM.nChar = sM.nTotal = 0; + sM.nAlloc = nInitBuf; + sM.xRealloc = xRealloc; + vxprintf(mout, &sM, useInternal, zFormat, ap); + if( xRealloc ){ + if( sM.zText==sM.zBase ){ + sM.zText = xRealloc(0, sM.nChar+1); + if( sM.zText ){ + memcpy(sM.zText, sM.zBase, sM.nChar+1); + } + }else if( sM.nAlloc>sM.nChar+10 ){ + char *zNew = xRealloc(sM.zText, sM.nChar+1); + if( zNew ){ + sM.zText = zNew; + } + } + } + return sM.zText; +} + +/* +** Realloc that is a real function, not a macro. +*/ +static void *printf_realloc(void *old, int size) +{ + return realloc(old,size); +} + +/* +** Print into memory. Use the internal %-conversion extensions. +*/ +SWITCH_DECLARE(char *) switch_vmprintf(const char *zFormat, va_list ap) +{ + char zBase[SWITCH_PRINT_BUF_SIZE]; + return base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap); +} + +/* +** Print into memory. Use the internal %-conversion extensions. +*/ +SWITCH_DECLARE(char *) switch_mprintf(const char *zFormat, ...) +{ + va_list ap; + char *z; + char zBase[SWITCH_PRINT_BUF_SIZE]; + va_start(ap, zFormat); + z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap); + va_end(ap); + return z; +} + +#ifdef __UNUSED__ +/* +** Print into memory. Omit the internal %-conversion extensions. +*/ +SWITCH_DECLARE(char *) switch_vmprintf(const char *zFormat, va_list ap) +{ + char zBase[SWITCH_PRINT_BUF_SIZE]; + return base_vprintf(printf_realloc, 0, zBase, sizeof(zBase), zFormat, ap); +} + +/* +** Print into memory. Omit the internal %-conversion extensions. +*/ +SWITCH_DECLARE(char *) switch_mprintf(const char *zFormat, ...) +{ + va_list ap; + char *z; + char zBase[SWITCH_PRINT_BUF_SIZE]; + va_start(ap, zFormat); + z = base_vprintf(printf_realloc, 0, zBase, sizeof(zBase), zFormat, ap); + va_end(ap); + return z; +} + +/* +** sqlite3_snprintf() works like snprintf() except that it ignores the +** current locale settings. This is important for SQLite because we +** are not able to use a "," as the decimal point in place of "." as +** specified by some locales. +*/ +SWITCH_DECLARE(char *) switch_snprintf(int n, char *zBuf, const char *zFormat, ...) +{ + char *z; + va_list ap; + + va_start(ap,zFormat); + z = base_vprintf(0, 0, zBuf, n, zFormat, ap); + va_end(ap); + return z; +} +#endif