/*
 * print.c	Routines to print stuff.
 *
 * Version:	$Id: print.c,v 1.26.2.1.2.2 2006/03/15 15:37:56 nbk Exp $
 *
 *   This 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.
 *
 *   This 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 this library; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 * Copyright 2000  The FreeRADIUS server project
 */

static const char rcsid[] = "$Id: print.c,v 1.26.2.1.2.2 2006/03/15 15:37:56 nbk Exp $";

#include	"autoconf.h"

#include	<stdio.h>
#include	<stdlib.h>
#include	<sys/types.h>
#include	<ctype.h>
#include	<string.h>

#include	"libradius.h"

/*
 *	Convert a string to something printable.
 *	The output string has to be _at least_ 4x the size
 *	of the input string!
 */
void librad_safeprint(char *in, int inlen, char *out, int outlen)
{
	unsigned char	*str = (unsigned char *)in;
	int		done = 0;
	int		sp = 0;

	if (inlen < 0) inlen = strlen(in);

	while (inlen-- > 0 && (done + 3) < outlen) {
		/*
		 *	Hack: never print trailing zero.
		 *	Some clients send strings with an off-by-one
		 *	length (confused with strings in C).
		 */
		if (inlen == 0 && *str == 0)
			break;

		sp = 0;

		switch (*str) {
			case '\\':
				sp = '\\';
				break;
			case '\r':
				sp = 'r';
				break;
			case '\n':
				sp = 'n';
				break;
			case '\t':
				sp = 't';
				break;
			default:
				if (*str < 32 || (*str >= 128)){
					snprintf(out, outlen, "\\%03o", *str);
					done += 4;
					out  += 4;
					outlen -= 4;
				} else {
					*out++ = *str;
					outlen--;
					done++;
				}
		}
		if (sp) {
			*out++ = '\\';
			*out++ = sp;
			outlen -= 2;
			done += 2;
		}
		str++;
	}
	*out = 0;
}


/*
 *  Print one value into a string.
 *  delimitst will define if strings and dates will be delimited by '"'
 */
int vp_prints_value(char * out, int outlen, VALUE_PAIR *vp, int delimitst)
{
	DICT_VALUE  *v;
	char        buf[1024];
	char        *a;
	time_t      t;
	struct tm   s_tm;

	out[0] = 0;
	if (!vp) return 0;

	switch (vp->type) {
		case PW_TYPE_STRING:
			/*
			 *  NAS-Port may have multiple integer values?
			 *  This is an internal server extension...
			 */
			if (vp->attribute == PW_NAS_PORT)
				a = (char *)vp->strvalue;
			else {
				if (delimitst && vp->flags.has_tag) {
				        /* Tagged attribute: print delimter and ignore tag */
				        buf[0] = '"';
					librad_safeprint((char *)(vp->strvalue),
							 vp->length, buf + 1, sizeof(buf) - 2);
					strcat(buf, "\"");
				} else if (delimitst) {
				        /* Non-tagged attribute: print delimter */
				        buf[0] = '"';
					librad_safeprint((char *)vp->strvalue,
							 vp->length, buf + 1, sizeof(buf) - 2);
					strcat(buf, "\"");
				} else {
				        /* Non-tagged attribute: no delimiter */
				        librad_safeprint((char *)vp->strvalue,
							 vp->length, buf, sizeof(buf));
				}
				a = buf;
			}
			break;
		case PW_TYPE_INTEGER:
		        if ( vp->flags.has_tag ) {
			        /* Attribute value has a tag, need to ignore it */
			        if ((v = dict_valbyattr(vp->attribute, (vp->lvalue & 0xffffff)))
				    != NULL)
				        a = v->name;
				else {
				        snprintf(buf, sizeof(buf), "%u", (vp->lvalue & 0xffffff));
				        a = buf;
				}
			} else {
			        /* Normal, non-tagged attribute */
			        if ((v = dict_valbyattr(vp->attribute, vp->lvalue))
				    != NULL)
				        a = v->name;
				else {
				        snprintf(buf, sizeof(buf), "%u", vp->lvalue);
					a = buf;
				}
			}
			break;
		case PW_TYPE_DATE:
			t = vp->lvalue;
			if (delimitst) {
			  strftime(buf, sizeof(buf), "\"%b %e %Y %H:%M:%S %Z\"",
				   localtime_r(&t, &s_tm));
			} else {
			  strftime(buf, sizeof(buf), "%b %e %Y %H:%M:%S %Z",
				   localtime_r(&t, &s_tm));
			}
			a = buf;
			break;
		case PW_TYPE_IPADDR:
			a = ip_ntoa(buf, vp->lvalue);
			break;
		case PW_TYPE_ABINARY:
#ifdef ASCEND_BINARY
		  a = buf;
		  print_abinary(vp, (unsigned char *)buf, sizeof(buf));
		  break;
#else
		  /* FALL THROUGH */
#endif
		case PW_TYPE_OCTETS:
		  strcpy(buf, "0x");
		  a = buf + 2;
		  for (t = 0; t < vp->length; t++) {
			sprintf(a, "%02x", vp->strvalue[t]);
			a += 2;
		  }
		  a = buf;
		  break;

		case PW_TYPE_IFID:
			a = ifid_ntoa(buf, sizeof(buf), vp->strvalue);
			break;

		case PW_TYPE_IPV6ADDR:
			a = ipv6_ntoa(buf, sizeof(buf), vp->strvalue);
			break;

		default:
			a = 0;
			break;
	}
	strNcpy(out, a?a:"UNKNOWN-TYPE", outlen);

	return strlen(out);
}

/*
 *  This is a hack, and has to be kept in sync with tokens.h
 */
static const char *vp_tokens[] = {
  "?",				/* T_INVALID */
  "EOL",			/* T_EOL */
  "{",
  "}",
  "(",
  ")",
  ",",
  ";",
  "+=",
  "-=",
  ":=",
  "=",
  "!=",
  ">=",
  ">",
  "<=",
  "<",
  "=~",
  "!~",
  "=*",
  "!*",
  "==",
  "#",
  "<BARE-WORD>",
  "<\"STRING\">",
  "<'STRING'>",
  "<`STRING`>"
};


/*
 *	Print one attribute and value into a string.
 */
int vp_prints(char *out, int outlen, VALUE_PAIR *vp)
{
	int		len;
	const char	*token = NULL;

	out[0] = 0;
	if (!vp) return 0;

	if (strlen(vp->name) + 3 > (size_t)outlen) {
		return 0;
	}

	if ((vp->operator > T_INVALID) &&
	    (vp->operator < T_TOKEN_LAST)) {
		token = vp_tokens[vp->operator];
	} else {
		token = "<INVALID-TOKEN>";
	}

	if( vp->flags.has_tag ) {

		snprintf(out, outlen, "%s:%d %s ", vp->name, vp->flags.tag,
			 token);

		len = strlen(out);
		vp_prints_value(out + len, outlen - len, vp, 1);

	} else {

	        snprintf(out, outlen, "%s %s ", vp->name, token);
		len = strlen(out);
		vp_prints_value(out + len, outlen - len, vp, 1);

	}

	return strlen(out);
}


/*
 *	Print one attribute and value.
 */
void vp_print(FILE *fp, VALUE_PAIR *vp)
{
	char	buf[1024];

	vp_prints(buf, sizeof(buf), vp);
	fputs(buf, fp);
}


/*
 *	Print a whole list of attributes, indented by a TAB
 *	and with a newline at the end.
 */
void vp_printlist(FILE *fp, VALUE_PAIR *vp)
{
	for (; vp; vp = vp->next) {
		fprintf(fp, "\t");
		vp_print(fp, vp);
		fprintf(fp, "\n");
	}
}

