Skip to content
Snippets Groups Projects
sexp-format.c 6.37 KiB
Newer Older
  • Learn to ignore specific revisions
  •  *
     * Writing s-expressions.
     */
    
    /* nettle, low-level cryptographics library
     *
     * Copyright (C) 2002 Niels Mller
     *  
     * The nettle library is free software; you can redistribute it and/or modify
     * it under the terms of the GNU Lesser General Public License as published by
     * the Free Software Foundation; either version 2.1 of the License, or (at your
     * option) any later version.
     * 
     * The nettle library 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 Lesser General Public
     * License for more details.
     * 
     * You should have received a copy of the GNU Lesser General Public License
     * along with the nettle library; see the file COPYING.LIB.  If not, write to
     * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
     * MA 02111-1307, USA.
     */
    
    #if HAVE_CONFIG_H
    
    #include <assert.h>
    
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    
    #if HAVE_LIBGMP
    # include "bignum.h"
    #endif
    
    
    /* Code copied from sexp-conv.c: sexp_put_length */
    static unsigned
    
    Niels Möller's avatar
    Niels Möller committed
    format_prefix(struct nettle_buffer *buffer,
    
      unsigned digit = 1;
      unsigned prefix_length = 1;
      
      for (;;)
        {
          unsigned next = digit * 10;
          if (next > length)
    	break;
    
          prefix_length++;
          digit = next;
        }
    
      if (buffer)
        {
          for (; digit; length %= digit, digit /= 10)
    	if (!NETTLE_BUFFER_PUTC(buffer, '0' + length / digit))
    	  return 0;
          
          if (!NETTLE_BUFFER_PUTC(buffer, ':'))
    	return 0;
        }
    
      return prefix_length + 1;
    
    static unsigned
    format_string(struct nettle_buffer *buffer,
    	      unsigned length, const uint8_t *s)
    {
      unsigned prefix_length = format_prefix(buffer, length);
      if (!prefix_length)
        return 0;
    
      if (buffer && !nettle_buffer_write(buffer, length, s))
        return 0;
    
      return prefix_length + length;
    }
    
    
    unsigned
    sexp_vformat(struct nettle_buffer *buffer, const char *format, va_list args)
    
    {
      unsigned nesting = 0;
    
      unsigned done = 0;
    
      for (;;)
        switch (*format++)
          {
    
    	{
    	  const char *start = format - 1;
    
    	  unsigned length = 1 + strcspn(format, "()% \t");
    
    	  unsigned output_length = format_string(buffer, length, start);
    	  if (!output_length)
    	    return 0;
    	  
    	  done += output_length;
    	  format = start + length;
    
    	  break;
    	}
    
    	assert(!nesting);
    	    
    	return done;
    
    
    	if (buffer && !NETTLE_BUFFER_PUTC(buffer, '('))
    	  return 0;
    
    	assert (nesting);
    	if (buffer && !NETTLE_BUFFER_PUTC(buffer, ')'))
    	  return 0;
    
    	done++;
    
    	{
    	  int nul_flag = 0;
    
    	  if (*format == '0')
    	    {
    	      format++;
    	      nul_flag = 1;
    
    	  switch (*format++)
    
    	    case '(':
    	    case ')':
    	      /* Allow unbalanced parenthesis */
    	      if (buffer && !NETTLE_BUFFER_PUTC(buffer, format[-1]))
    		return 0;
    	      done++;
    	      break;
    	      
    
    	    case 's':
    	      {
    		const char *s;
    		unsigned length;
    
    		unsigned output_length;
    
    		
    		if (nul_flag)
    		  {
    		    s = va_arg(args, const char *);
    		    length = strlen(s);
    		  }
    		else
    		  {
    		    length = va_arg(args, unsigned);
    		    s = va_arg(args, const char *);
    		  }
    		
    
    		output_length = format_string(buffer, length, s);
    		if (!output_length)
    
    		done += output_length;
    
    		break;
    	      }
    	    case 't':
    	      {
    		const char *s;
    		unsigned length;
    
    		unsigned output_length;
    
    		
    		if (nul_flag)
    		  {
    		    s = va_arg(args, const char *);
    		    if (!s)
    		      break;
    		    
    		    length = strlen(s);
    		  }
    		else
    		  {
    		    length = va_arg(args, unsigned);
    		    s = va_arg(args, const char *);
    		    if (!s)
    		      break;
    		  }
    		
    		if (buffer && !NETTLE_BUFFER_PUTC(buffer, '['))
    		  return 0;
    		done++;
    		
    
    		output_length = format_string(buffer, length, s);
    
    		if (!output_length)
    
    		done += output_length;
    		
    
    		if (buffer && !NETTLE_BUFFER_PUTC(buffer, ']'))
    		  return 0;
    		done++;
    		
    		break;
    	      }
    
    	    case 'l':
    	      {
    		const char *s;
    		unsigned length;
    		
    		if (nul_flag)
    		  {
    		    s = va_arg(args, const char *);
    		    length = strlen(s);
    		  }
    		else
    		  {
    		    length = va_arg(args, unsigned);
    		    s = va_arg(args, const char *);
    		  }
    
    		if (buffer && !nettle_buffer_write(buffer, length, s))
    		  return 0;
    
    		done += length;
    		break;
    	      }
    	    case 'i':
    	      {
    		uint32_t x = va_arg(args, uint32_t);
    		unsigned length;
    
    		if (x < 0x80)
    		  length = 1;
    		else if (x < 0x8000L)
    		  length = 2;
    		else if (x < 0x800000L)
    		  length = 3;
    		else if (x < 0x80000000L)
    		  length = 4;
    		else
    		  length = 5;
    	      
    		if (buffer && !(NETTLE_BUFFER_PUTC(buffer, '0' + length)
    				&& NETTLE_BUFFER_PUTC(buffer, ':')))
    		  return 0;
    
    		done += (2 + length);
    
    		if (buffer)
    		  switch(length)
    		    {
    		    case 5:
    		      /* Leading byte needed for the sign. */
    		      if (!NETTLE_BUFFER_PUTC(buffer, 0))
    			return 0;
    		      /* Fall through */
    		    case 4:
    		      if (!NETTLE_BUFFER_PUTC(buffer, x >> 24))
    			return 0;
    		      /* Fall through */
    		    case 3:
    		      if (!NETTLE_BUFFER_PUTC(buffer, (x >> 16) & 0xff))
    			return 0;
    		      /* Fall through */
    		    case 2:
    		      if (!NETTLE_BUFFER_PUTC(buffer, (x >> 8) & 0xff))
    			return 0;
    		      /* Fall through */
    		    case 1:
    		      if (!NETTLE_BUFFER_PUTC(buffer, x & 0xff))
    			return 0;
    		      break;
    		    default:
    		      abort();
    		    }
    		break;
    	      }
    	    case 'b':
    	      {
    
    		const MP_INT *n = va_arg(args, const MP_INT *);
    		unsigned length;
    		unsigned prefix_length;
    
    		length = nettle_mpz_sizeinbase_256_s(n);
    		prefix_length = format_prefix(buffer, length);
    		if (!prefix_length)
    		  return 0;
    
    		done += prefix_length;
    
    		if (buffer)
    		  {
    		    uint8_t *space = nettle_buffer_space(buffer, length);
    		    if (!space)
    		      return 0;
    
    		    nettle_mpz_get_str_256(length, space, n);
    		  }
    
    		done += length;
    
    #else /* ! HAVE_LIBGMP */
    
    #endif /* ! HAVE_LIBGMP */
    
    
    unsigned
    sexp_format(struct nettle_buffer *buffer, const char *format, ...)
    {
      va_list args;
      unsigned done;
      
      va_start(args, format);
      done = sexp_vformat(buffer, format, args);
      va_end(args);
    
      return done;
    }