From f8e6feb6bc9d7064acdaeaafb9004113ff9c1278 Mon Sep 17 00:00:00 2001 From: hniksic Date: Wed, 5 Dec 2001 21:53:21 -0800 Subject: [PATCH] [svn] Fix varargs usage in logvprintf. Published in . --- src/ChangeLog | 7 ++ src/log.c | 192 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 121 insertions(+), 78 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index d9b79e62..f8966093 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,10 @@ +2001-12-06 Hrvoje Niksic + + * log.c (logvprintf): Restructure to allow being called multiple + times. + (logprintf): Call logvprintf in a loop. + (debug_logprintf): Ditto. + 2001-12-06 Hrvoje Niksic * gen_sslfunc.c (ssl_init_prng): Allow the user to disable EGD by diff --git a/src/log.c b/src/log.c index f43351dc..2c1e42a6 100644 --- a/src/log.c +++ b/src/log.c @@ -281,84 +281,92 @@ logputs (enum log_options o, const char *s) needs_flushing = 1; } +struct logvprintf_state { + char *bigmsg; + int expected_size; + int allocated; +}; + /* Print a message to the log. A copy of message will be saved to - saved_log, for later reusal by log_dump(). */ + saved_log, for later reusal by log_dump(). -static void -logvprintf (enum log_options o, const char *fmt, va_list args) -{ - CHECK_VERBOSE (o); - CANONICALIZE_LOGFP_OR_RETURN; + It is not possible to code this function in a "natural" way, using + a loop, because of the braindeadness of the varargs API. + Specifically, each call to vsnprintf() must be preceded by va_start + and followed by va_end. And this is possible only in the function + that contains the `...' declaration. The alternative would be to + use va_copy, but that's not portable. */ - /* Originally, we first used vfprintf(), and then checked whether - the message needs to be stored with vsprintf(). However, Watcom - C didn't like ARGS being used twice, so now we first vsprintf() - the message, and then fwrite() it to LOGFP. */ +static int +logvprintf (struct logvprintf_state *state, const char *fmt, va_list args) +{ + char smallmsg[128]; + char *write_ptr = smallmsg; + int available_size = sizeof (smallmsg); + int numwritten; if (!save_log_p) { /* In the simple case just call vfprintf(), to avoid needless allocation and games with vsnprintf(). */ vfprintf (logfp, fmt, args); + goto flush; } - else + + if (state->allocated != 0) { - char smallmsg[128]; - char *bigmsg = NULL; - int available_size = sizeof (smallmsg); - char *write_ptr = smallmsg; + write_ptr = state->bigmsg; + available_size = state->allocated; + } - while (1) - { - /* The GNU coding standards advise not to rely on the return - value of sprintf(). However, vsnprintf() is a relatively - new function missing from legacy systems. Therefore it's - safe to assume that its return value is meaningful. On - the systems where vsnprintf() is not available, we use - the implementation from snprintf.c which does return the - correct value. */ - int numwritten = vsnprintf (write_ptr, available_size, fmt, args); - - /* vsnprintf() will not step over the limit given by - available_size. If it fails, it will return either -1 - (POSIX?) or the number of characters that *would have* - been written, if there had been enough room. In the - former case, we double the available_size and malloc() to - get a larger buffer, and try again. In the latter case, - we use the returned information to build a buffer of the - correct size. */ - - if (numwritten == -1) - { - /* Writing failed, and we don't know the needed size. - Try again with doubled size. */ - available_size <<= 1; - bigmsg = xrealloc (bigmsg, available_size); - write_ptr = bigmsg; - } - else if (numwritten >= available_size) - { - /* Writing failed, but we know exactly how much space we - need. */ - available_size = numwritten + 1; - bigmsg = xrealloc (bigmsg, available_size); - write_ptr = bigmsg; - } - else - { - /* Writing succeeded. */ - break; - } - } - saved_append (write_ptr); - fputs (write_ptr, logfp); - if (bigmsg) - xfree (bigmsg); + /* The GNU coding standards advise not to rely on the return value + of sprintf(). However, vsnprintf() is a relatively new function + missing from legacy systems. Therefore I consider it safe to + assume that its return value is meaningful. On the systems where + vsnprintf() is not available, we use the implementation from + snprintf.c which does return the correct value. */ + numwritten = vsnprintf (write_ptr, available_size, fmt, args); + + /* vsnprintf() will not step over the limit given by available_size. + If it fails, it will return either -1 (POSIX?) or the number of + characters that *would have* been written, if there had been + enough room. In the former case, we double the available_size + and malloc() to get a larger buffer, and try again. In the + latter case, we use the returned information to build a buffer of + the correct size. */ + + if (numwritten == -1) + { + /* Writing failed, and we don't know the needed size. Try + again with doubled size. */ + int newsize = available_size << 1; + state->bigmsg = xrealloc (state->bigmsg, newsize); + state->allocated = newsize; + return 0; } + else if (numwritten >= available_size) + { + /* Writing failed, but we know exactly how much space we + need. */ + int newsize = numwritten + 1; + state->bigmsg = xrealloc (state->bigmsg, newsize); + state->allocated = newsize; + return 0; + } + + /* Writing succeeded. */ + saved_append (write_ptr); + fputs (write_ptr, logfp); + if (state->bigmsg) + xfree (state->bigmsg); + + flush: if (flush_log_p) logflush (); else needs_flushing = 1; + + return 1; } /* Flush LOGFP. Useful while flushing is disabled. */ @@ -392,6 +400,21 @@ log_set_flush (int flush) } } +#ifdef WGET_USE_STDARG +# define VA_START_1(arg1_type, arg1, args) va_start(args, arg1) +# define VA_START_2(arg1_type, arg1, arg2_type, arg2, args) va_start(args, arg2) +#else /* not WGET_USE_STDARG */ +# define VA_START_1(arg1_type, arg1, args) do { \ + va_start (args); \ + arg1 = va_arg (args, arg1_type); \ +} while (0) +# define VA_START_2(arg1_type, arg1, arg2_type, arg2, args) do { \ + va_start (args); \ + arg1 = va_arg (args, arg1_type); \ + arg2 = va_arg (args, arg2_type); \ +} while (0) +#endif /* not WGET_USE_STDARG */ + /* Portability with pre-ANSI compilers makes these two functions look like @#%#@$@#$. */ @@ -405,20 +428,29 @@ logprintf (va_alist) #endif /* not WGET_USE_STDARG */ { va_list args; + struct logvprintf_state lpstate; + int done; + #ifndef WGET_USE_STDARG enum log_options o; const char *fmt; -#endif -#ifdef WGET_USE_STDARG - va_start (args, fmt); -#else - va_start (args); - o = va_arg (args, enum log_options); - fmt = va_arg (args, char *); -#endif - logvprintf (o, fmt, args); + /* Perform a "dry run" of VA_START_2 to get the value of O. */ + VA_START_2 (enum log_options, o, char *, fmt, args); va_end (args); +#endif + + CHECK_VERBOSE (o); + CANONICALIZE_LOGFP_OR_RETURN; + + memset (&lpstate, '\0', sizeof (lpstate)); + do + { + VA_START_2 (enum log_options, o, char *, fmt, args); + done = logvprintf (&lpstate, fmt, args); + va_end (args); + } + while (!done); } #ifdef DEBUG @@ -439,15 +471,19 @@ debug_logprintf (va_alist) #ifndef WGET_USE_STDARG const char *fmt; #endif + struct logvprintf_state lpstate; + int done; -#ifdef WGET_USE_STDARG - va_start (args, fmt); -#else - va_start (args); - fmt = va_arg (args, char *); -#endif - logvprintf (LOG_ALWAYS, fmt, args); - va_end (args); + CANONICALIZE_LOGFP_OR_RETURN; + + memset (&lpstate, '\0', sizeof (lpstate)); + do + { + VA_START_1 (char *, fmt, args); + done = logvprintf (&lpstate, fmt, args); + va_end (args); + } + while (!done); } } #endif /* DEBUG */ -- 2.39.2