]> granicus.if.org Git - sudo/commitdiff
KTH snprintf()/asprintf() for systems w/o them
authorTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 15 Jul 1999 14:21:02 +0000 (14:21 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 15 Jul 1999 14:21:02 +0000 (14:21 +0000)
snprintf.c [new file with mode: 0644]

diff --git a/snprintf.c b/snprintf.c
new file mode 100644 (file)
index 0000000..dd44495
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+ * Copyright (c) 1995-1997, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the Kungliga Tekniska
+ *      Högskolan and its contributors.
+ * 
+ * 4. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $KTH: snprintf.c,v 1.19 1999/03/27 16:32:57 joda Exp $
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif /* STDC_HEADERS */
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#include <ctype.h>
+#ifdef __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+#include <sys/param.h>
+
+#include "compat.h"
+
+enum format_flags {
+    minus_flag     =  1,
+    plus_flag      =  2,
+    space_flag     =  4,
+    alternate_flag =  8,
+    zero_flag      = 16
+};
+
+/*
+ * Common state
+ */
+
+struct state {
+  unsigned char *str;
+  unsigned char *s;
+  unsigned char *theend;
+  size_t sz;
+  size_t max_sz;
+  int (*append_char) __P((struct state *, unsigned char));
+  int (*reserve) __P((struct state *, size_t));
+  /* XXX - methods */
+};
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#ifndef HAVE_VSNPRINTF
+static int
+sn_reserve (state, n)
+  struct state *state;
+  size_t n;
+{
+  return state->s + n > state->theend;
+}
+
+static int
+#ifdef __STDC__
+sn_append_char (struct state *state, unsigned char c)
+#else
+sn_append_char (state, c)
+  struct state *state;
+  unsigned char c;
+#endif
+{
+  if (sn_reserve (state, 1)) {
+    return 1;
+  } else {
+    *state->s++ = c;
+    return 0;
+  }
+}
+#endif
+
+static int
+as_reserve (state, n)
+  struct state *state;
+  size_t n;
+{
+  if (state->s + n > state->theend) {
+    int off = state->s - state->str;
+    unsigned char *tmp;
+
+    if (state->max_sz && state->sz >= state->max_sz)
+      return 1;
+
+    state->sz = MAX(state->sz * 2, state->sz + n);
+    if (state->max_sz)
+      state->sz = MIN(state->sz, state->max_sz);
+    tmp = realloc (state->str, state->sz);
+    if (tmp == NULL)
+      return 1;
+    state->str = tmp;
+    state->s = state->str + off;
+    state->theend = state->str + state->sz - 1;
+  }
+  return 0;
+}
+
+static int
+#ifdef __STDC__
+as_append_char (struct state *state, unsigned char c)
+#else
+as_append_char (state, c)
+  struct state *state;
+  unsigned char c;
+#endif
+{
+  if(as_reserve (state, 1))
+    return 1;
+  else {
+    *state->s++ = c;
+    return 0;
+  }
+}
+
+static int
+append_number(state, num, base, rep, width, prec, flags, minusp)
+  struct state *state;
+  unsigned long num;
+  unsigned base;
+  unsigned char *rep;
+  int width;
+  int prec;
+  int flags;
+  int minusp;
+{
+  int len = 0;
+  int i;
+
+  /* given precision, ignore zero flag */
+  if(prec != -1)
+    flags &= ~zero_flag;
+  else
+    prec = 1;
+  /* zero value with zero precision -> "" */
+  if(prec == 0 && num == 0)
+    return 0;
+  do{
+    if((*state->append_char)(state, rep[num % base]))
+      return 1;
+    len++;
+    num /= base;
+  }while(num);
+  prec -= len;
+  /* pad with prec zeros */
+  while(prec-- > 0){
+    if((*state->append_char)(state, '0'))
+      return 1;
+    len++;
+  }
+  /* add length of alternate prefix (added later) to len */
+  if(flags & alternate_flag && (base == 16 || base == 8))
+    len += base / 8;
+  /* pad with zeros */
+  if(flags & zero_flag){
+    width -= len;
+    if(minusp || (flags & space_flag) || (flags & plus_flag))
+      width--;
+    while(width-- > 0){
+      if((*state->append_char)(state, '0'))
+       return 1;
+      len++;
+    }
+  }
+  /* add alternate prefix */
+  if(flags & alternate_flag && (base == 16 || base == 8)){
+    if(base == 16)
+      if((*state->append_char)(state, rep[10] + 23)) /* XXX */
+       return 1;
+    if((*state->append_char)(state, '0'))
+      return 1;
+  }
+  /* add sign */
+  if(minusp){
+    if((*state->append_char)(state, '-'))
+      return 1;
+    len++;
+  } else if(flags & plus_flag) {
+    if((*state->append_char)(state, '+'))
+      return 1;
+    len++;
+  } else if(flags & space_flag) {
+    if((*state->append_char)(state, ' '))
+      return 1;
+    len++;
+  }
+  if(flags & minus_flag)
+    /* swap before padding with spaces */
+    for(i = 0; i < len / 2; i++){
+      char c = state->s[-i-1];
+      state->s[-i-1] = state->s[-len+i];
+      state->s[-len+i] = c;
+    }
+  width -= len;
+  while(width-- > 0){
+    if((*state->append_char)(state,  ' '))
+      return 1;
+    len++;
+  }
+  if(!(flags & minus_flag))
+    /* swap after padding with spaces */
+    for(i = 0; i < len / 2; i++){
+      char c = state->s[-i-1];
+      state->s[-i-1] = state->s[-len+i];
+      state->s[-len+i] = c;
+    }
+    
+  return 0;
+}
+
+static int
+append_string (state, arg, width, prec, flags)
+  struct state *state;
+  unsigned char *arg;
+  int width;
+  int prec;
+  int flags;
+{
+  if(prec != -1)
+    width -= prec;
+  else
+    width -= strlen(arg);
+  if(!(flags & minus_flag))
+    while(width-- > 0)
+      if((*state->append_char) (state, ' '))
+       return 1;
+  if (prec != -1) {
+    while (*arg && prec--)
+      if ((*state->append_char) (state, *arg++))
+       return 1;
+  } else {
+    while (*arg)
+      if ((*state->append_char) (state, *arg++))
+       return 1;
+  }
+  if(flags & minus_flag)
+    while(width-- > 0)
+      if((*state->append_char) (state, ' '))
+       return 1;
+  return 0;
+}
+
+static int
+append_char(state, arg, width, flags)
+  struct state *state;
+  unsigned char arg;
+  int width;
+  int flags;
+{
+  while(!(flags & minus_flag) && --width > 0)
+    if((*state->append_char) (state, ' '))
+      return 1;
+    
+  if((*state->append_char) (state, arg))
+    return 1;
+  while((flags & minus_flag) && --width > 0)
+    if((*state->append_char) (state, ' '))
+      return 1;
+    
+  return 0;
+}
+
+/*
+ * This can't be made into a function...
+ */
+
+#define PARSE_INT_FORMAT(res, arg, unsig) \
+if (long_flag) \
+     res = va_arg(arg, unsig long); \
+else if (short_flag) \
+     res = va_arg(arg, unsig short); \
+else \
+     res = va_arg(arg, unsig int)
+
+/*
+ * zyxprintf - return 0 or -1
+ */
+
+static int
+xyzprintf (state, char_format, ap)
+  struct state *state;
+  const char *char_format;
+  va_list ap;
+{
+  const unsigned char *format = (const unsigned char *)char_format;
+  unsigned char c;
+
+  while((c = *format++)) {
+    if (c == '%') {
+      int flags      = 0;
+      int width      = 0;
+      int prec       = -1;
+      int long_flag  = 0;
+      int short_flag = 0;
+
+      /* flags */
+      while((c = *format++)){
+       if(c == '-')
+         flags |= minus_flag;
+       else if(c == '+')
+         flags |= plus_flag;
+       else if(c == ' ')
+         flags |= space_flag;
+       else if(c == '#')
+         flags |= alternate_flag;
+       else if(c == '0')
+         flags |= zero_flag;
+       else
+         break;
+      }
+      
+      if((flags & space_flag) && (flags & plus_flag))
+       flags ^= space_flag;
+
+      if((flags & minus_flag) && (flags & zero_flag))
+       flags ^= zero_flag;
+
+      /* width */
+      if (isdigit(c))
+       do {
+         width = width * 10 + c - '0';
+         c = *format++;
+       } while(isdigit(c));
+      else if(c == '*') {
+       width = va_arg(ap, int);
+       c = *format++;
+      }
+
+      /* precision */
+      if (c == '.') {
+       prec = 0;
+       c = *format++;
+       if (isdigit(c))
+         do {
+           prec = prec * 10 + c - '0';
+           c = *format++;
+         } while(isdigit(c));
+       else if (c == '*') {
+         prec = va_arg(ap, int);
+         c = *format++;
+       }
+      }
+
+      /* size */
+
+      if (c == 'h') {
+       short_flag = 1;
+       c = *format++;
+      } else if (c == 'l') {
+       long_flag = 1;
+       c = *format++;
+      }
+
+      switch (c) {
+      case 'c' :
+       if(append_char(state, va_arg(ap, int), width, flags))
+         return -1;
+       break;
+      case 's' :
+       if (append_string(state,
+                         va_arg(ap, unsigned char*),
+                         width,
+                         prec, 
+                         flags))
+         return -1;
+       break;
+      case 'd' :
+      case 'i' : {
+       long arg;
+       unsigned long num;
+       int minusp = 0;
+
+       PARSE_INT_FORMAT(arg, ap, signed);
+
+       if (arg < 0) {
+         minusp = 1;
+         num = -arg;
+       } else
+         num = arg;
+
+       if (append_number (state, num, 10, "0123456789",
+                          width, prec, flags, minusp))
+         return -1;
+       break;
+      }
+      case 'u' : {
+       unsigned long arg;
+
+       PARSE_INT_FORMAT(arg, ap, unsigned);
+
+       if (append_number (state, arg, 10, "0123456789",
+                          width, prec, flags, 0))
+         return -1;
+       break;
+      }
+      case 'o' : {
+       unsigned long arg;
+
+       PARSE_INT_FORMAT(arg, ap, unsigned);
+
+       if (append_number (state, arg, 010, "01234567",
+                          width, prec, flags, 0))
+         return -1;
+       break;
+      }
+      case 'x' : {
+       unsigned long arg;
+
+       PARSE_INT_FORMAT(arg, ap, unsigned);
+
+       if (append_number (state, arg, 0x10, "0123456789abcdef",
+                          width, prec, flags, 0))
+         return -1;
+       break;
+      }
+      case 'X' :{
+       unsigned long arg;
+
+       PARSE_INT_FORMAT(arg, ap, unsigned);
+
+       if (append_number (state, arg, 0x10, "0123456789ABCDEF",
+                          width, prec, flags, 0))
+         return -1;
+       break;
+      }
+      case 'p' : {
+       unsigned long arg = (unsigned long)va_arg(ap, void*);
+
+       if (append_number (state, arg, 0x10, "0123456789ABCDEF",
+                          width, prec, flags, 0))
+         return -1;
+       break;
+      }
+      case 'n' : {
+       int *arg = va_arg(ap, int*);
+       *arg = state->s - state->str;
+       break;
+      }
+      case '%' :
+       if ((*state->append_char)(state, c))
+         return -1;
+       break;
+      default :
+       if (   (*state->append_char)(state, '%')
+           || (*state->append_char)(state, c))
+         return -1;
+       break;
+      }
+    } else
+      if ((*state->append_char) (state, c))
+       return -1;
+  }
+  return 0;
+}
+
+#ifndef HAVE_SNPRINTF
+int
+#ifdef __STDC__
+snprintf (char *str, size_t sz, const char *format, ...)
+#else
+snprintf (va_alist)
+  va_dcl
+#endif
+{
+  va_list args;
+  int ret;
+
+#ifdef __STDC__
+  va_start(args, format);
+#else
+  char *str;
+  size_t sz;
+  const char *format;
+
+  va_start(args);
+  str = va_arg(args, char *);
+  sz = va_arg(args, size_t);
+  format = va_arg(args, const char *);
+#endif
+  ret = vsnprintf (str, sz, format, args);
+
+#ifdef PARANOIA
+  {
+    int ret2;
+    char *tmp;
+
+    tmp = malloc (sz);
+    if (tmp == NULL)
+      abort ();
+
+    ret2 = vsprintf (tmp, format, args);
+    if (ret != ret2 || strcmp(str, tmp))
+      abort ();
+    free (tmp);
+  }
+#endif
+
+  va_end(args);
+  return ret;
+}
+#endif
+
+#ifndef HAVE_VASNPRINTF
+int
+vasnprintf (ret, max_sz, format, args)
+  char **ret;
+  size_t max_sz;
+  const char *format;
+  va_list args;
+{
+  int st;
+  size_t len;
+  struct state state;
+
+  state.max_sz = max_sz;
+  state.sz     = 1;
+  state.str    = malloc(state.sz);
+  if (state.str == NULL) {
+    *ret = NULL;
+    return -1;
+  }
+  state.s = state.str;
+  state.theend = state.s + state.sz - 1;
+  state.append_char = as_append_char;
+  state.reserve     = as_reserve;
+
+  st = xyzprintf (&state, format, args);
+  if (st) {
+    free (state.str);
+    *ret = NULL;
+    return -1;
+  } else {
+    char *tmp;
+
+    *state.s = '\0';
+    len = state.s - state.str;
+    tmp = realloc (state.str, len+1);
+    if (tmp == NULL) {
+      free (state.str);
+      *ret = NULL;
+      return -1;
+    }
+    *ret = tmp;
+    return len;
+  }
+}
+#endif
+
+#ifndef HAVE_ASPRINTF
+int
+#ifdef __STDC__
+asprintf (char **ret, const char *format, ...)
+#else
+asprintf (va_alist)
+  va_dcl
+#endif
+{
+  va_list args;
+  int val;
+
+#ifdef __STDC__
+  va_start(args, format);
+#else
+  char **ret;
+  const char *format;
+
+  va_start(args);
+  ret = va_arg(args, char **);
+  format = va_arg(args, const char *);
+#endif
+  val = vasprintf (ret, format, args);
+
+#ifdef PARANOIA
+  {
+    int ret2;
+    char *tmp;
+    tmp = malloc (val + 1);
+    if (tmp == NULL)
+      abort ();
+
+    ret2 = vsprintf (tmp, format, args);
+    if (val != ret2 || strcmp(*ret, tmp))
+      abort ();
+    free (tmp);
+  }
+#endif
+
+  va_end(args);
+  return val;
+}
+#endif
+
+#ifndef HAVE_ASNPRINTF
+int
+#ifdef __STDC__
+asnprintf (char **ret, size_t max_sz, const char *format, ...)
+#else
+asnprintf (va_alist)
+  va_dcl
+#endif
+{
+  va_list args;
+  int val;
+
+#ifdef __STDC__
+  va_start(args, format);
+#else
+  char **ret;
+  size_t max_sz;
+  const char *format;
+
+  va_start(args);
+  ret = va_arg(args, char **);
+  max_sz = va_arg(args, size_t);
+  format = va_arg(args, const char *);
+#endif
+  val = vasnprintf (ret, max_sz, format, args);
+
+#ifdef PARANOIA
+  {
+    int ret2;
+    char *tmp;
+    tmp = malloc (val + 1);
+    if (tmp == NULL)
+      abort ();
+
+    ret2 = vsprintf (tmp, format, args);
+    if (val != ret2 || strcmp(*ret, tmp))
+      abort ();
+    free (tmp);
+  }
+#endif
+
+  va_end(args);
+  return val;
+}
+#endif
+
+#ifndef HAVE_VASPRINTF
+int
+vasprintf (ret, format, args)
+  char **ret;
+  const char *format;
+  va_list args;
+{
+  return vasnprintf (ret, 0, format, args);
+}
+#endif
+
+
+#ifndef HAVE_VSNPRINTF
+int
+vsnprintf (str, sz, format, args)
+  char *str;
+  size_t sz;
+  const char *format;
+  va_list args;
+{
+  struct state state;
+  int ret;
+
+  state.max_sz = 0;
+  state.sz     = sz;
+  state.str    = str;
+  state.s      = str;
+  state.theend = str + sz - 1;
+  state.append_char = sn_append_char;
+  state.reserve     = sn_reserve;
+
+  ret = xyzprintf (&state, format, args);
+  *state.s = '\0';
+  if (ret)
+    return sz;
+  else
+    return state.s - state.str;
+}
+#endif