From cfb37cd15f7218cce06dc9fb77428623aa29f3a4 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Tue, 2 Jan 2007 20:33:03 +0000 Subject: [PATCH] Added silc_snprintf, silc_vsnprintf, silc_asprintf, silc_vasprintf to lib/silcutil/silcsnprintf.[ch]. Added lib/silcske/silcconauth.h header documentation. --- CHANGES | 6 + TODO | 4 +- includes/silc.h.in | 3 +- lib/silcske/silcconnauth.h | 175 +++++++- lib/silcutil/Makefile.ad | 8 +- lib/silcutil/silcfsm.h | 4 +- lib/silcutil/silcsnprintf.c | 769 ++++++++++++++++++++++++++++++++++++ lib/silcutil/silcsnprintf.h | 82 ++++ lib/silcutil/silcstrutil.c | 9 - lib/silcutil/silcstrutil.h | 20 - lib/silcutil/silcutil.c | 2 +- 11 files changed, 1034 insertions(+), 48 deletions(-) create mode 100644 lib/silcutil/silcsnprintf.c create mode 100644 lib/silcutil/silcsnprintf.h diff --git a/CHANGES b/CHANGES index 20856172..926223bc 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +Tue Jan 2 17:18:46 EET 2007 Pekka Riikonen + + * Added lib/silcutil/silcsnprintf.[ch]. Added in addition + of silc_snprintf, silc_vsnprintf, silc_asprintf and + silc_vasprintf. + Sat Dec 30 23:23:17 EET 2006 Pekka Riikonen * Added preliminary Symbian support. Changes around the source diff --git a/TODO b/TODO index 22cd60b3..486a18ab 100644 --- a/TODO +++ b/TODO @@ -119,7 +119,7 @@ lib/silcskr be. -lib/silcske/silcske.[ch] ****PARTLY DONE**** +lib/silcske/silcske.[ch] ****DONE**** ======================== o IV Included flag support in SKE (***DONE) @@ -127,7 +127,7 @@ lib/silcske/silcske.[ch] ****PARTLY DONE**** o UDP transport changes; retransmission support by using exponential backoff algorithm. (***DONE) - o SilcConnAuth header file documentation. + o SilcConnAuth header file documentation. (***DONE) lib/silccrypt ****PARTLY DONE**** diff --git a/includes/silc.h.in b/includes/silc.h.in index e633638d..accf5225 100644 --- a/includes/silc.h.in +++ b/includes/silc.h.in @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2006 Pekka Riikonen + Copyright (C) 1997 - 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -222,6 +222,7 @@ extern "C" { /* SILC util library includes */ #include "silcstack.h" #include "silcmemory.h" +#include "silcsnprintf.h" /* Math library includes */ #include "silcmp.h" diff --git a/lib/silcske/silcconnauth.h b/lib/silcske/silcconnauth.h index 76ceb536..5f48fcb4 100644 --- a/lib/silcske/silcconnauth.h +++ b/lib/silcske/silcconnauth.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2005 Pekka Riikonen + Copyright (C) 2005 - 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,27 +21,77 @@ * * DESCRIPTION * - * SILC Connection Authentication protocol is closely related to the SILC - * Key Exchange protocol. After successful key exchange, additional - * authentication is usually required to gain access to remote server or - * service. Connection authentication may be based on passphrase or - * digital signatures. It is also possible to have no authentication at - * all. + * SILC Connection Authenetication protocol API is used to perform the + * connection authentication after successful SILC Key Exchange protocol. + * The interface supports authentication based on passphrases and digital + * signatures. It is also possible to have no authentication at all. * ***/ #ifndef SILCCONNAUTH_H #define SILCCONNAUTH_H +/****s* silcske/SilcConnAuthAPI/SilcConnAuth + * + * NAME + * + * typedef struct SilcConnAuthStruct *SilcConnAuth; + * + * DESCRIPTION + * + * The connection authentication context allocated by silc_connauth_alloc + * and given as arguments to all silc_connauth_* functions. It is freed + * by silc_connauth_free. + * + ***/ typedef struct SilcConnAuthStruct *SilcConnAuth; +/****d* silcske/SilcConnAuthAPI/SilcConnectionType + * + * NAME + * + * typedef enum { ... } SilcConnectionType; + * + * DESCRIPTION + * + * The type of the connection. + * + * SOURCE + */ typedef enum { - SILC_CONN_UNKNOWN = 0, + SILC_CONN_UNKNOWN = 0, /* Unknown type, cannot be sent */ SILC_CONN_CLIENT = 1, /* Client connection */ SILC_CONN_SERVER = 2, /* Server connection */ SILC_CONN_ROUTER = 3 /* Router connection */ } SilcConnectionType; +/***/ +/****f* silcske/SilcConnAuthAPI/SilcConnAuthGetAuthData + * + * SYNOPSIS + * + * typedef SilcBool + * (*SilcConnAuthGetAuthData)(SilcConnAuth connauth, + * SilcConnectionType conn_type, + * unsigned char **passphrase, + * SilcUInt32 *passphrase_len, + * SilcSKR *repository, + * void *context); + * + * DESCRIPTION + * + * Authentication callback to retrieve the authentication data from the + * application. This is responder callback. If the authentication + * method is passphrase it must be returned to `passphrase' pointer. + * If it is digital signatures the key repository pointer must be + * returned into `repository' pointer, which the library will use to + * find the correct public key to verify the digital signature. + * + * If this connection is not configured at all this returns FALSE which + * will result into authentication failure. Otherwise TRUE must be + * returned. + * + ***/ typedef SilcBool (*SilcConnAuthGetAuthData)(SilcConnAuth connauth, SilcConnectionType conn_type, unsigned char **passphrase, @@ -49,15 +99,98 @@ typedef SilcBool (*SilcConnAuthGetAuthData)(SilcConnAuth connauth, SilcSKR *repository, void *context); +/****f* silcske/SilcConnAuthAPI/SilcConnAuthCompletion + * + * SYNOPSIS + * + * typedef void (*SilcConnAuthCompletion)(SilcConnAuth connauth, + * SilcBool success, + * void *context); + * + * DESCRIPTION + * + * Completion callback called to indicated the result of the connection + * authentication protocol. If the `success' is FALSE the authentication + * was a failure. The authentication protocol is over after this callback + * is called. + * + ***/ typedef void (*SilcConnAuthCompletion)(SilcConnAuth connauth, SilcBool success, void *context); -SilcConnAuth silc_connauth_alloc(SilcSchedule schedule, - SilcSKE ske, +/****f* silcske/SilcConnAuthAPI/silc_connauth_alloc + * + * SYNOPSIS + * + * SilcConnAuth silc_connauth_alloc(SilcSchedule schedule, SilcSKE ske, + * SilcUInt32 timeout_secs); + * + * DESCRIPTION + * + * Allocates the connection authentication protocol context. The `ske' + * is the successfully completed key exchange context. The `timeout_secs' + * is the maximum time we are waiting for the protocol to finish before + * it is timedout. Returns NULL on error. + * + ***/ +SilcConnAuth silc_connauth_alloc(SilcSchedule schedule, SilcSKE ske, SilcUInt32 timeout_secs); + +/****f* silcske/SilcConnAuthAPI/silc_connauth_free + * + * SYNOPSIS + * + * void silc_connauth_free(SilcConnAuth connauth); + * + * DESCRIPTION + * + * Frees the connection authentication protocol context `connauth'. + * + ***/ void silc_connauth_free(SilcConnAuth connauth); + +/****f* silcske/SilcConnAuthAPI/silc_connauth_get_ske + * + * SYNOPSIS + * + * SilcSKE silc_connauth_get_ske(SilcConnAuth connauth); + * + * DESCRIPTION + * + * Returns the associated SilcSKE context from the `connauth'. It is the + * pointer given as argument to silc_connauth_alloc. + * + ***/ SilcSKE silc_connauth_get_ske(SilcConnAuth connauth); + +/****f* silcske/SilcConnAuthAPI/silc_connauth_initiator + * + * SYNOPSIS + * + * SilcAsyncOperation + * silc_connauth_initiator(SilcConnAuth connauth, + * SilcConnectionType conn_type, + * SilcAuthMethod auth_method, void *auth_data, + * SilcUInt32 auth_data_len, + * SilcConnAuthCompletion completion, + * void *context); + * + * DESCRIPTION + * + * Starts the connection authentication protocol as initiator. The + * `conn_type' is the type of connection we are. The `auth_method' is + * the authentication method. If it is SILC_AUTH_PASSWORD the `auth_data' + * and `auth_data_len' is the passphrase and its length, respectively. + * If it is SILC_AUTH_PUBLIC_KEY the `auth_data' is the SilcPrivateKey + * used to produce the digital signature. The `auth_data_len' is 0. + * The `completion' with `context' will be called after the protocol + * has completed. + * + * This returns SilcAsyncOperation context which can be used to abort + * the protocol before it is completed. Returns NULL on error. + * + ***/ SilcAsyncOperation silc_connauth_initiator(SilcConnAuth connauth, SilcConnectionType conn_type, @@ -65,6 +198,28 @@ silc_connauth_initiator(SilcConnAuth connauth, SilcUInt32 auth_data_len, SilcConnAuthCompletion completion, void *context); + +/****f* silcske/SilcConnAuthAPI/silc_connauth_responder + * + * SYNOPSIS + * + * SilcAsyncOperation + * silc_connauth_responder(SilcConnAuth connauth, + * SilcConnAuthGetAuthData get_auth_data, + * SilcConnAuthCompletion completion, + * void *context); + * + * DESCRIPTION + * + * Starts the connection authentication protocol as responder. The + * `get_auth_data' is called to retrieve the authentication data for + * this connection. The `completion' will be called after the protocol + * has completed. + * + * This returns SilcAsyncOperation context which can be used to abort + * the protocol before it is completed. Returns NULL on error. + * + ***/ SilcAsyncOperation silc_connauth_responder(SilcConnAuth connauth, SilcConnAuthGetAuthData get_auth_data, diff --git a/lib/silcutil/Makefile.ad b/lib/silcutil/Makefile.ad index cb837bd3..7629855d 100644 --- a/lib/silcutil/Makefile.ad +++ b/lib/silcutil/Makefile.ad @@ -3,7 +3,7 @@ # # Author: Pekka Riikonen # -# Copyright (C) 2000 - 2006 Pekka Riikonen +# Copyright (C) 2000 - 2007 Pekka Riikonen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -57,7 +57,8 @@ libsilcutil_la_SOURCES = \ silcasync.c \ silctime.c \ silcmime.c \ - silcstack.c + silcstack.c \ + silcsnprintf.c #ifdef SILC_DIST_TOOLKIT include_HEADERS = \ @@ -92,7 +93,8 @@ include_HEADERS = \ silcasync.h \ silcasync_i.h \ silcstack.h \ - silcstack_i.h + silcstack_i.h \ + silcsnprintf.h SILC_EXTRA_DIST = tests #endif SILC_DIST_TOOLKIT diff --git a/lib/silcutil/silcfsm.h b/lib/silcutil/silcfsm.h index 29ec9016..cc922051 100644 --- a/lib/silcutil/silcfsm.h +++ b/lib/silcutil/silcfsm.h @@ -133,12 +133,12 @@ typedef struct SilcFSMObject SilcFSMThreadStruct; * } * ***/ -#ifndef SILC_FSM_SMALL_STACK +#if defined(SILC_DEBUG) #define SILC_FSM_CONTINUE \ return fsm->next_state(fsm, fsm->fsm_context, fsm->state_context); #else #define SILC_FSM_CONTINUE return SILC_FSM_ST_CONTINUE; -#endif /* SILC_FSM_SMALL_STACK */ +#endif /* SILC_DEBUG */ /****d* silcutil/SilcFSMAPI/SILC_FSM_YIELD * diff --git a/lib/silcutil/silcsnprintf.c b/lib/silcutil/silcsnprintf.c new file mode 100644 index 00000000..633bdf1d --- /dev/null +++ b/lib/silcutil/silcsnprintf.c @@ -0,0 +1,769 @@ +/* + * Copyright Patrick Powell 1995 + * This code is based on code written by Patrick Powell (papowell@astart.com) + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + * + * More Recently: + * Brandon Long 9/15/96 for mutt 0.43 + * This was ugly. It is still ugly. I opted out of floating point + * numbers, but the formatter understands just about everything + * from the normal C string format, at least as far as I can tell from + * the Solaris 2.5 printf(3S) man page. + * + * Brandon Long 10/22/97 for mutt 0.87.1 + * Ok, added some minimal floating point support, which means this + * probably requires libm on most operating systems. Don't yet + * support the exponent (e,E) and sigfig (g,G). Also, fmtint() + * was pretty badly broken, it just wasn't being exercised in ways + * which showed it, so that's been fixed. Also, formated the code + * to mutt conventions, and removed dead code left over from the + * original. Also, there is now a builtin-test, just compile with: + * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm + * and run snprintf for results. + * + * Thomas Roessler 01/27/98 for mutt 0.89i + * The PGP code was using unsigned hexadecimal formats. + * Unfortunately, unsigned formats simply didn't work. + * + * Michael Elkins 03/05/98 for mutt 0.90.8 + * The original code assumed that both snprintf() and vsnprintf() were + * missing. Some systems only have snprintf() but not vsnprintf(), so + * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. + * + * Andrew Tridgell (tridge@samba.org) Oct 1998 + * fixed handling of %.0f + * added test for HAVE_LONG_DOUBLE + * + * tridge@samba.org, idra@samba.org, April 2001 + * got rid of fcvt code (twas buggy and made testing harder) + * added C99 semantics + * + **************************************************************/ + +#include "silc.h" + +#ifdef HAVE_LONG_DOUBLE +#define LDOUBLE long double +#else +#define LDOUBLE double +#endif + +#ifdef HAVE_LONG_LONG +#define LLONG long long +#else +#define LLONG long +#endif + +/* + * dopr(): poor man's version of doprintf + */ + +/* format read states */ +#define DP_S_DEFAULT 0 +#define DP_S_FLAGS 1 +#define DP_S_MIN 2 +#define DP_S_DOT 3 +#define DP_S_MAX 4 +#define DP_S_MOD 5 +#define DP_S_CONV 6 +#define DP_S_DONE 7 + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UP (1 << 5) +#define DP_F_UNSIGNED (1 << 6) + +/* Conversion Flags */ +#define DP_C_SHORT 1 +#define DP_C_LONG 2 +#define DP_C_LDOUBLE 3 +#define DP_C_LLONG 4 + +#define char_to_int(p) ((p)- '0') +#ifndef MAX +#define MAX(p,q) (((p) >= (q)) ? (p) : (q)) +#endif + +static size_t dopr(char *buffer, size_t maxlen, const char *format, + va_list args_in); +static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max); +static void fmtint(char *buffer, size_t *currlen, size_t maxlen, + long value, int base, int min, int max, int flags); +static void fmtfp(char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags); +static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); + +static size_t dopr(char *buffer, size_t maxlen, const char *format, + va_list args_in) +{ + char ch; + LLONG value; + LDOUBLE fvalue; + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + size_t currlen; + va_list args; + + silc_va_copy(args, args_in); + + state = DP_S_DEFAULT; + currlen = flags = cflags = min = 0; + max = -1; + ch = *format++; + + while (state != DP_S_DONE) { + if (ch == '\0') + state = DP_S_DONE; + + switch(state) { + case DP_S_DEFAULT: + if (ch == '%') + state = DP_S_FLAGS; + else + dopr_outch (buffer, &currlen, maxlen, ch); + ch = *format++; + break; + case DP_S_FLAGS: + switch (ch) { + case '-': + flags |= DP_F_MINUS; + ch = *format++; + break; + case '+': + flags |= DP_F_PLUS; + ch = *format++; + break; + case ' ': + flags |= DP_F_SPACE; + ch = *format++; + break; + case '#': + flags |= DP_F_NUM; + ch = *format++; + break; + case '0': + flags |= DP_F_ZERO; + ch = *format++; + break; + default: + state = DP_S_MIN; + break; + } + break; + case DP_S_MIN: + if (isdigit((unsigned char)ch)) { + min = 10*min + char_to_int (ch); + ch = *format++; + } else if (ch == '*') { + min = va_arg (args, int); + ch = *format++; + state = DP_S_DOT; + } else { + state = DP_S_DOT; + } + break; + case DP_S_DOT: + if (ch == '.') { + state = DP_S_MAX; + ch = *format++; + } else { + state = DP_S_MOD; + } + break; + case DP_S_MAX: + if (isdigit((unsigned char)ch)) { + if (max < 0) + max = 0; + max = 10*max + char_to_int (ch); + ch = *format++; + } else if (ch == '*') { + max = va_arg (args, int); + ch = *format++; + state = DP_S_MOD; + } else { + state = DP_S_MOD; + } + break; + case DP_S_MOD: + switch (ch) { + case 'h': + cflags = DP_C_SHORT; + ch = *format++; + break; + case 'l': + cflags = DP_C_LONG; + ch = *format++; + if (ch == 'l') { /* It's a long long */ + cflags = DP_C_LLONG; + ch = *format++; + } + break; + case 'L': + cflags = DP_C_LDOUBLE; + ch = *format++; + break; + default: + break; + } + state = DP_S_CONV; + break; + case DP_S_CONV: + switch (ch) { + case 'd': + case 'i': + if (cflags == DP_C_SHORT) + value = va_arg (args, int); + else if (cflags == DP_C_LONG) + value = va_arg (args, long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, LLONG); + else + value = va_arg (args, int); + fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'o': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (long)va_arg (args, unsigned LLONG); + else + value = (long)va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); + break; + case 'u': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (LLONG)va_arg (args, unsigned LLONG); + else + value = (long)va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'X': + flags |= DP_F_UP; + case 'x': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (LLONG)va_arg (args, unsigned LLONG); + else + value = (long)va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); + break; + case 'f': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + /* um, floating point? */ + fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'E': + flags |= DP_F_UP; + case 'e': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'G': + flags |= DP_F_UP; + case 'g': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'c': + dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); + break; + case 's': + strvalue = va_arg (args, char *); + if (!strvalue) strvalue = "(NULL)"; + if (max == -1) { + max = strlen(strvalue); + } + if (min > 0 && max >= 0 && min > max) max = min; + fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); + break; + case 'p': + strvalue = va_arg (args, void *); + fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); + break; + case 'n': + if (cflags == DP_C_SHORT) { + short int *num; + num = va_arg (args, short int *); + *num = currlen; + } else if (cflags == DP_C_LONG) { + long int *num; + num = va_arg (args, long int *); + *num = (long int)currlen; + } else if (cflags == DP_C_LLONG) { + LLONG *num; + num = va_arg (args, LLONG *); + *num = (LLONG)currlen; + } else { + int *num; + num = va_arg (args, int *); + *num = currlen; + } + break; + case '%': + dopr_outch (buffer, &currlen, maxlen, ch); + break; + case 'w': + /* not supported yet, treat as next char */ + ch = *format++; + break; + default: + /* Unknown, skip */ + break; + } + ch = *format++; + state = DP_S_DEFAULT; + flags = cflags = min = 0; + max = -1; + break; + case DP_S_DONE: + break; + default: + /* hmm? */ + break; /* some picky compilers need this */ + } + } + if (maxlen != 0) { + if (currlen < maxlen - 1) + buffer[currlen] = '\0'; + else if (maxlen > 0) + buffer[maxlen - 1] = '\0'; + } + + return currlen; +} + +static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max) +{ + int padlen, strln; /* amount to pad */ + int cnt = 0; + + if (value == 0) { + value = ""; + } + + for (strln = 0; value[strln]; ++strln); /* strlen */ + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justify */ + + while ((padlen > 0) && (cnt < max)) { + dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + ++cnt; + } + while (*value && (cnt < max)) { + dopr_outch (buffer, currlen, maxlen, *value++); + ++cnt; + } + while ((padlen < 0) && (cnt < max)) { + dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + ++cnt; + } +} + +/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ + +static void fmtint(char *buffer, size_t *currlen, size_t maxlen, + long value, int base, int min, int max, int flags) +{ + int signvalue = 0; + unsigned long uvalue; + char convert[20]; + int place = 0; + int spadlen = 0; /* amount to space pad */ + int zpadlen = 0; /* amount to zero pad */ + int caps = 0; + + if (max < 0) + max = 0; + + uvalue = value; + + if(!(flags & DP_F_UNSIGNED)) { + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } else { + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else if (flags & DP_F_SPACE) + signvalue = ' '; + } + } + + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ + + do { + convert[place++] = + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; + uvalue = (uvalue / (unsigned)base ); + } while(uvalue && (place < 20)); + if (place == 20) place--; + convert[place] = 0; + + zpadlen = max - place; + spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); + if (zpadlen < 0) zpadlen = 0; + if (spadlen < 0) spadlen = 0; + if (flags & DP_F_ZERO) { + zpadlen = MAX(zpadlen, spadlen); + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; /* Left Justifty */ + + /* Spaces */ + while (spadlen > 0) { + dopr_outch (buffer, currlen, maxlen, ' '); + --spadlen; + } + + /* Sign */ + if (signvalue) + dopr_outch (buffer, currlen, maxlen, signvalue); + + /* Zeros */ + if (zpadlen > 0) { + while (zpadlen > 0) { + dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + } + + /* Digits */ + while (place > 0) + dopr_outch (buffer, currlen, maxlen, convert[--place]); + + /* Left Justified spaces */ + while (spadlen < 0) { + dopr_outch (buffer, currlen, maxlen, ' '); + ++spadlen; + } +} + +static LDOUBLE abs_val(LDOUBLE value) +{ + LDOUBLE result = value; + + if (value < 0) + result = -value; + + return result; +} + +static LDOUBLE POW10(int exp) +{ + LDOUBLE result = 1; + + while (exp) { + result *= 10; + exp--; + } + + return result; +} + +static LLONG ROUND(LDOUBLE value) +{ + LLONG intpart; + + intpart = (LLONG)value; + value = value - intpart; + if (value >= 0.5) intpart++; + + return intpart; +} + +/* a replacement for modf that doesn't need the math library. Should + be portable, but slow */ +static double my_modf(double x0, double *iptr) +{ + int i; + long l; + double x = x0; + double f = 1.0; + + for (i=0;i<100;i++) { + l = (long)x; + if (l <= (x+1) && l >= (x-1)) break; + x *= 0.1; + f *= 10.0; + } + + if (i == 100) { + /* yikes! the number is beyond what we can handle. + What do we do? */ + (*iptr) = 0; + return 0; + } + + if (i != 0) { + double i2; + double ret; + + ret = my_modf(x0-l*f, &i2); + (*iptr) = l*f + i2; + return ret; + } + + (*iptr) = l; + return x - (*iptr); +} + + +static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags) +{ + int signvalue = 0; + double ufvalue; + char iconvert[311]; + char fconvert[311]; + int iplace = 0; + int fplace = 0; + int padlen = 0; /* amount to pad */ + int zpadlen = 0; + int caps = 0; + int idx; + double intpart; + double fracpart; + double temp; + + /* + * AIX manpage says the default is 0, but Solaris says the default + * is 6, and sprintf on AIX defaults to 6 + */ + if (max < 0) + max = 6; + + ufvalue = abs_val (fvalue); + + if (fvalue < 0) { + signvalue = '-'; + } else { + if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ + signvalue = '+'; + } else { + if (flags & DP_F_SPACE) + signvalue = ' '; + } + } + +#if 0 + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ +#endif + +#if 0 + if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */ +#endif + + /* + * Sorry, we only support 16 digits past the decimal because of our + * conversion method + */ + if (max > 16) + max = 16; + + /* We "cheat" by converting the fractional part to integer by + * multiplying by a factor of 10 + */ + + temp = ufvalue; + my_modf(temp, &intpart); + + fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); + + if (fracpart >= POW10(max)) { + intpart++; + fracpart -= POW10(max); + } + + + /* Convert integer part */ + do { + temp = intpart*0.1; + my_modf(temp, &intpart); + idx = (int) ((temp -intpart +0.05)* 10.0); + /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ + /* printf ("%llf, %f, %x\n", temp, intpart, idx); */ + iconvert[iplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; + } while (intpart && (iplace < 311)); + if (iplace == 311) iplace--; + iconvert[iplace] = 0; + + /* Convert fractional part */ + if (fracpart) + { + do { + temp = fracpart*0.1; + my_modf(temp, &fracpart); + idx = (int) ((temp -fracpart +0.05)* 10.0); + /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */ + /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */ + fconvert[fplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; + } while(fracpart && (fplace < 311)); + if (fplace == 311) fplace--; + } + fconvert[fplace] = 0; + + /* -1 for decimal point, another -1 if we are printing a sign */ + padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); + zpadlen = max - fplace; + if (zpadlen < 0) zpadlen = 0; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justifty */ + + if ((flags & DP_F_ZERO) && (padlen > 0)) { + if (signvalue) { + dopr_outch (buffer, currlen, maxlen, signvalue); + --padlen; + signvalue = 0; + } + while (padlen > 0) { + dopr_outch (buffer, currlen, maxlen, '0'); + --padlen; + } + } + while (padlen > 0) { + dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + } + if (signvalue) + dopr_outch (buffer, currlen, maxlen, signvalue); + + while (iplace > 0) + dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); + + /* + * Decimal point. This should probably use locale to find the correct + * char to print out. + */ + if (max > 0) { + dopr_outch (buffer, currlen, maxlen, '.'); + + while (zpadlen > 0) { + dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + + while (fplace > 0) + dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); + } + + while (padlen < 0) { + dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + } +} + +static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c) +{ + if (*currlen < maxlen) { + buffer[(*currlen)] = c; + } + (*currlen)++; +} + +int silc_vsnprintf(char *str, size_t count, const char *fmt, va_list args) +{ + if (str != NULL) + str[0] = 0; + return dopr(str, count, fmt, args); +} + +int silc_snprintf(char *str, size_t count, const char *fmt, ...) +{ + size_t ret; + va_list ap; + + va_start(ap, fmt); + ret = silc_vsnprintf(str, count, fmt, ap); + va_end(ap); + return ret; +} + +int silc_vasprintf(char **ptr, const char *format, va_list ap) +{ + int ret; + va_list ap2; + + silc_va_copy(ap2, ap); + + ret = silc_vsnprintf(NULL, 0, format, ap2); + if (ret <= 0) return ret; + + (*ptr) = (char *)silc_malloc(ret+1); + if (!*ptr) return -1; + + silc_va_copy(ap2, ap); + + ret = silc_vsnprintf(*ptr, ret+1, format, ap2); + + return ret; +} + +int silc_asprintf(char **ptr, const char *format, ...) +{ + va_list ap; + int ret; + + *ptr = NULL; + va_start(ap, format); + ret = silc_vasprintf(ptr, format, ap); + va_end(ap); + + return ret; +} diff --git a/lib/silcutil/silcsnprintf.h b/lib/silcutil/silcsnprintf.h new file mode 100644 index 00000000..d15b1c9f --- /dev/null +++ b/lib/silcutil/silcsnprintf.h @@ -0,0 +1,82 @@ +/* + + silcsnprintf.h + + Author: Pekka Riikonen + + Copyright (C) 2007 Pekka Riikonen + + This program 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; version 2 of the License. + + This program 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. + +*/ + +#ifndef SILCSNPRINTF_H +#define SILCSNPRINTF_H + +/****f* silcutil/SilcSnprintf/silc_snprintf + * + * SYNOPSIS + * + * int silc_snprintf(char *str, size_t count, const char *fmt, ...); + * + * DESCRIPTION + * + * Outputs string into `str' of maximum of size `count' including the + * trailing '\0' according to the `fmt'. The `fmt' is equivalent to + * snprintf(3) and printf(3) formatting. Returns the number of character + * in `str' or negative value on error. + * + ***/ +int silc_snprintf(char *str, size_t count, const char *fmt, ...); + +/****f* silcutil/SilcSnprintf/silc_vsnprintf + * + * SYNOPSIS + * + * int silc_vsnprintf(char *str, size_t count, const char *fmt, + * va_list args) + * + * DESCRIPTION + * + * Same as silc_snprintf but takes the argument for the formatting from + * the `args' variable argument list. + * + ***/ +int silc_vsnprintf(char *str, size_t count, const char *fmt, va_list args); + +/****f* silcutil/SilcSnprintf/silc_asprintf + * + * SYNOPSIS + * + * int silc_asprintf(char **ptr, const char *format, ...) + * + * DESCRIPTION + * + * Same as silc_snprintf but allocates a string large enough to hold the + * output including the trailing '\0'. The caller must free the `ptr'. + * + ***/ +int silc_asprintf(char **ptr, const char *format, ...); + +/****f* silcutil/SilcSnprintf/silc_vasprintf + * + * SYNOPSIS + * + * int silc_vasprintf(char **ptr, const char *format, va_list ap) + * + * DESCRIPTION + * + * Same as silc_asprintf but takes the argument from the `ap' variable + * argument list. + * + ***/ +int silc_vasprintf(char **ptr, const char *format, va_list ap); + +#endif /* SILCSNPRINTF_H */ diff --git a/lib/silcutil/silcstrutil.c b/lib/silcutil/silcstrutil.c index 5ba4c02e..1c537c11 100644 --- a/lib/silcutil/silcstrutil.c +++ b/lib/silcutil/silcstrutil.c @@ -170,15 +170,6 @@ unsigned char *silc_pem_decode(unsigned char *pem, SilcUInt32 pem_len, return data; } -#ifndef HAVE_SNPRINTF -/* Outputs string according to the `format'. */ - -int silc_snprintf(char *str, SilcUInt32 size, const char *format, ...) -{ - -} -#endif /* HAVE_SNPRINTF */ - /* Concatenates the `src' into `dest'. If `src_len' is more than the size of the `dest' (minus NULL at the end) the `src' will be truncated to fit. */ diff --git a/lib/silcutil/silcstrutil.h b/lib/silcutil/silcstrutil.h index 1fdc0fd1..26defd31 100644 --- a/lib/silcutil/silcstrutil.h +++ b/lib/silcutil/silcstrutil.h @@ -111,26 +111,6 @@ char *silc_pem_encode_file(unsigned char *data, SilcUInt32 data_len); unsigned char *silc_pem_decode(unsigned char *pem, SilcUInt32 pem_len, SilcUInt32 *ret_len); -/****f* silcutil/SilcStrStrUtilAPI/silc_snprintf - * - * SYNOPSIS - * - * int silc_snprintf(char *str, SilcUInt32 size, const char *format, ...); - * - * DESCRIPTION - * - * Produces output string according to the `format'. The formatting - * is equivalent to silc_snprintf(3) and sprintf(3). Returns the number of - * characters output into `str', at most `size' characters including the - * trailing '\0' character. Returns negative value on error. - * - ***/ -#ifndef SILC_SNPRINTF -int silc_snprintf(char *str, SilcUInt32 size, const char *format, ...); -#else -#define silc_snprintf snprintf -#endif /* SILC_SNPRINTF */ - /****f* silcutil/SilcStrStrUtilAPI/silc_strncat * * SYNOPSIS diff --git a/lib/silcutil/silcutil.c b/lib/silcutil/silcutil.c index 5fd5de84..a63efa9f 100644 --- a/lib/silcutil/silcutil.c +++ b/lib/silcutil/silcutil.c @@ -236,7 +236,7 @@ char *silc_format(char *fmt, ...) memset(buf, 0, sizeof(buf)); va_start(args, fmt); - vsnprintf(buf, sizeof(buf) - 1, fmt, args); + silc_vsnprintf(buf, sizeof(buf) - 1, fmt, args); va_end(args); return strdup(buf); -- 2.24.0