]> sjero.net Git - wget/commitdiff
[svn] Fix varargs usage in logvprintf.
authorhniksic <devnull@localhost>
Thu, 6 Dec 2001 05:53:21 +0000 (21:53 -0800)
committerhniksic <devnull@localhost>
Thu, 6 Dec 2001 05:53:21 +0000 (21:53 -0800)
Published in <sxs667knbe1.fsf@florida.arsdigita.de>.

src/ChangeLog
src/log.c

index d9b79e62b213c886dbf72de1fc40bef7e8c47aba..f8966093bd8acb7f93c01dcbce053f0cd81b199c 100644 (file)
@@ -1,3 +1,10 @@
+2001-12-06  Hrvoje Niksic  <hniksic@arsdigita.com>
+
+       * log.c (logvprintf): Restructure to allow being called multiple
+       times.
+       (logprintf): Call logvprintf in a loop.
+       (debug_logprintf): Ditto.
+
 2001-12-06  Hrvoje Niksic  <hniksic@arsdigita.com>
 
        * gen_sslfunc.c (ssl_init_prng): Allow the user to disable EGD by
index f43351dc6a8c25a4b204efdb6191396a2fe79f7d..2c1e42a6fe2c2c0e8c31f6c5e6a02fcdca0e3f01 100644 (file)
--- 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 */