//-----------------------------------------------------------------------------
// Copyright © 2003 - Philip Howard - All rights reserved
//
// 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; either version 2
// of the License, or (at your option) any later version.
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//-----------------------------------------------------------------------------
// package	libh/string
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This file is best viewed using a fixed spaced font such as Courier
// and in a display at least 120 columns wide.
//-----------------------------------------------------------------------------

#include "string_lib.h"

__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macros	str_app_ul_bin
//		str_app_ul_oct
//		str_app_ul_dec
//		str_app_ul_cet
//
// purpose	Append to the first string (given with maximum available space)
//		a string of digits representing the unsigned long value
//		converted in the named base.
//
//		Do append only if all the digits can be appended.
//
// arguments	1 (char *) pointer to target string
//		2 (size_t) maximum space in target string
//		3 (unsigned long) the number to convert
//		4 (int) minimum number of digits
//		5 (int) (char) padding character
//
// returns	(size_t) new length of string, or ~0 for error
//
// note		Contents of available space after the termination character
//		are destroyed if the append fails due to insufficient space.
//		Nothing should be kept there, anyway.
//-----------------------------------------------------------------------------
#define str_app_ul_bin(t,s,v,m,p)	str_app_ul((t),(s),(v),(m),(p),2,(const char*)(NULL))
#define str_app_ul_oct(t,s,v,m,p)	str_app_ul((t),(s),(v),(m),(p),8,(const char*)(NULL))
#define str_app_ul_dec(t,s,v,m,p)	str_app_ul((t),(s),(v),(m),(p),10,(const char*)(NULL))
#define str_app_ul_cet(t,s,v,m,p)	str_app_ul((t),(s),(v),(m),(p),16,(const char*)(NULL))

__FMACRO_END__

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	str_app_ul
//
// purpose	Append to the first string (given with maximum available space)
//		a string of digits representing the unsigned long value.
//
//		Do append only if all the digits can be appended.
//
// arguments	1 (char *) pointer to target string
//		2 (size_t) maximum space in target string
//		3 (unsigned long) the number to convert
//		4 (int) minimum number of digits
//		5 (int) (char) padding character
//		6 (int) conversion base
//		7 (const char *) alternate digit set
//
// returns	(size_t) new length of string, or ~0 for error
//
// note		Contents of available space after the termination character
//		are destroyed if the append fails due to insufficient space.
//		Nothing should be kept there, anyway.
//-----------------------------------------------------------------------------
size_t
str_app_ul (
    char *		arg_target
    ,
    size_t		arg_length
    ,
    unsigned long	arg_value
    ,
    int			arg_min
    ,
    int			arg_pad_ch
    ,
    int			arg_base
    ,
    const char *	arg_digit_set
    )
__PROTO_END__
{
    static const char	default_digit_set	[] = "0123456789abcdefghijklmnopqrstuvwxyz";
    const char *	digit_set		;
    char *		digit_ptr		;
    unsigned int	base			;
    int			digit_count		;
    char		convert_space		[CHAR_BIT * sizeof (unsigned long) + 2];

    //--------------------------------------------------------
    // If padding is given as the null character, use a blank.
    //--------------------------------------------------------
    if ( arg_pad_ch == 0 ) arg_pad_ch = ' ';

    //-------------------------------------------
    // Use standard digits if no alternate given.
    // Check requested base for digits available.
    //-------------------------------------------
    {
	unsigned int	max_base	;

	if ( arg_digit_set ) {
	    digit_set = arg_digit_set;
	    max_base = strlen( digit_set );
	    base = ( arg_base == 0 ) ? max_base : arg_base;
	} else {
	    digit_set = default_digit_set;
	    max_base = strlen( default_digit_set );
	    base = ( arg_base == 0 ) ? 10 : arg_base;
	}
	if ( base < 2 || max_base < base ) return ~0;
    }

    //---------------------------------------------
    // Convert number backwards in plenty of space.
    //---------------------------------------------
    {
	unsigned long	un_value	;

	//-- Start with a terminator at the end.
	* ( digit_ptr = convert_space + CHAR_BIT * sizeof (unsigned long) + 1 ) = 0;
	digit_count = 0;

	//-- Use a copy value.
	un_value = arg_value;

	//-- Always convert lowest digit even if 0.
	* -- digit_ptr = digit_set[un_value % base];
	++ digit_count;

	un_value /= base;
	while ( un_value || digit_count < arg_min ) {
	    if ( digit_ptr < convert_space ) break;
	    * -- digit_ptr = digit_set[un_value % base];
	    ++ digit_count;
	    un_value /= base;
	}
    }

    //-----------------------------
    // Fill with padding character.
    //-----------------------------
    while ( digit_count < arg_min && digit_ptr >= convert_space ) {
        * -- digit_ptr = arg_pad_ch;
        ++ digit_count;
    }

    //-----------------------------------
    // Append these digits to the string.
    //-----------------------------------
    return str_app_str( arg_target, arg_length, digit_ptr );
}

