// *********************************************************************************
// 
// Copyright (c) Microsoft Corporation
// 
// Module Name:
// 
//		ShowResults.c 
//  
// Abstract:
//  
//		This modules  has functions which are  to shoow formatted Results on screen.
//  
// Author:
// 
//		Sunil G.V.N. Murali (murali.sunil@wipro.com) 24-Sep-2000
//  
// Revision History:
//  
//		Sunil G.V.N. Murali (murali.sunil@wipro.com) 01-Sep-2000 : Created It.
//  
// *********************************************************************************

#include "pch.h"
#include "cmdline.h"
#include "cmdlineres.h"

//
// constants / defines / enumerations
//

//
// private functions ... used only within this file
//

// ***************************************************************************
// Routine Description:
//		Prepares the pszBuffer string by taking values from arrValues and 
//      formate these values as per szFormat string.   
//
// Arguments:
//      [ in ] arrValues	: values to be formated.
//      [ out ] pszBuffer	: output string
//      [ in ] dwLength		: string length.
//      [ in ] szFormat		: format 
//      [ in ] szSeperator	: Seperator string
//
// Return Value:
//      NONE
// 
// ***************************************************************************
VOID __PrepareString( TARRAY arrValues, 
					  LPTSTR pszBuffer, DWORD dwLength, 
					  LPCTSTR szFormat, LPCTSTR szSeperator )
{
	// local variables
	DWORD dw = 0;
	DWORD dwCount = 0;
	LPTSTR pszTemp = NULL;
	LPTSTR pszValue = NULL;

	//
	// kick off 

	// init
	lstrcpy( pszBuffer, NULL_STRING );
	dwCount = DynArrayGetCount( arrValues );

	// allocate memory for buffers
	pszTemp = __calloc( dwLength + 5, sizeof( TCHAR ) );
	pszValue = __calloc( dwLength + 5, sizeof( TCHAR ) );
	if ( pszTemp == NULL || pszValue == NULL )
	{
		// release memories
		__free( pszTemp );
		__free( pszValue );
		return;
	}

	//
	// traverse thru the list of the values and concatenate them 
	// to the destination buffer
	for( dw = 0; dw < dwCount; dw++ )
	{
		// get the current value into the temporary string buffer
		DynArrayItemAsStringEx( arrValues, dw, pszValue, dwLength );

		// concatenate the temporary string to the original buffer
		FORMAT_STRING( pszTemp, szFormat, _X( pszValue ) );
		lstrcat( pszBuffer, pszTemp );
		dwLength -= StringLengthInBytes( pszTemp );
		
		// check whether this is the last value or not
		if ( dw + 1 < dwCount )
		{
			// there are some more values
			// check whether is space for adding the seperator or not
			if ( dwLength < (DWORD) StringLengthInBytes( szSeperator ) )
			{
				// no more space available ... break
				break;
			}
			else
			{
				// add the seperator and update the length accordingly
				lstrcat( pszBuffer, szSeperator );
				dwLength -= StringLengthInBytes( szSeperator );
			}
		}
	}

	// release memories
	__free( pszTemp );
	__free( pszValue );
}

// ***************************************************************************
// Routine Description:
//		Gets the value from arrRecord and copies it to pszValue using 
//      proper format. 
//
// Arguments:
//      [ in ] pColumn			:  format info.
//      [ in ] dwColumn			:  no of columns
//      [ in ] arrRecord		: value to be formatted
//      [ out ] pszValue		: output string
//      [ in ] szArraySeperator : seperator used.
//  
// Return Value:
//      NONE
// 
// ***************************************************************************
VOID __GetValue( PTCOLUMNS pColumn, 
				 DWORD dwColumn, TARRAY arrRecord, 
				 LPTSTR pszValue, LPCTSTR szArraySeperator )
{
	// local variables
	LPVOID pData = NULL;						// data to be passed to formatter function
	TARRAY arrTemp = NULL;
	LPCTSTR pszTemp = NULL;
	__STRING_64 szFormat = NULL_STRING;	// format

	// variables used in formatting time
	DWORD dwReturn = 0;
	SYSTEMTIME systime = { 0 };

	// init first
	lstrcpy( pszValue, NULL_STRING );

	// get the column value and do formatting appropriately
	switch( pColumn->dwFlags & SR_TYPE_MASK )
	{
	case SR_TYPE_STRING:
		{
			// identify the format to be used
			if ( pColumn->dwFlags & SR_VALUEFORMAT )
				lstrcpy( szFormat, pColumn->szFormat );
			else
				lstrcpy( szFormat, _T( "%s" ) );		// default format

			// copy the value to the temporary buffer based on the flags specified
			if ( pColumn->dwFlags & SR_ARRAY )
			{
				// get the value into buffer first - AVOIDING PREFIX BUGS
				arrTemp = DynArrayItem( arrRecord, dwColumn );
				if ( arrTemp == NULL )
					return;

				// form the array of values into one single string with ',' seperated
				__PrepareString( arrTemp, pszValue, pColumn->dwWidth, szFormat, szArraySeperator );
			}
			else
			{
				// get the value into buffer first - AVOIDING PREFIX BUGS
				pszTemp = DynArrayItemAsString( arrRecord, dwColumn );
				if ( pszTemp == NULL )
					return;

				// now copy the value into buffer
				FORMAT_STRING( pszValue, szFormat, _X( pszTemp ) );
			}

			// switch case completed
			break;
		}

	case SR_TYPE_NUMERIC:
		{
			// identify the format to be used
			if ( pColumn->dwFlags & SR_VALUEFORMAT )
				lstrcpy( szFormat, pColumn->szFormat );
			else
				lstrcpy( szFormat, _T( "%d" ) );		// default format

			// copy the value to the temporary buffer based on the flags specified
			if ( pColumn->dwFlags & SR_ARRAY )
			{
				// get the value into buffer first - AVOIDING PREFIX BUGS
				arrTemp = DynArrayItem( arrRecord, dwColumn );
				if ( arrTemp == NULL )
					return;

				// form the array of values into one single string with ',' seperated
				__PrepareString( arrTemp, pszValue, pColumn->dwWidth, szFormat, _T( ", " ) );
			}
			else 
			{
				// get the value using format specified
				FORMAT_STRING( pszValue, szFormat, DynArrayItemAsDWORD( arrRecord, dwColumn ) );
			}

			// switch case completed
			break;
		}
	
	case SR_TYPE_FLOAT:
		{
			// identify the format to be used
			// NOTE: for this type, format needs to be specified
			// if not, value displayed is unpredictable
			if ( pColumn->dwFlags & SR_VALUEFORMAT )
				lstrcpy( szFormat, pColumn->szFormat );
			else
				lstrcpy( szFormat, _T( "%f" ) );		// default format

			// copy the value to the temporary buffer based on the flags specified
			if ( pColumn->dwFlags & SR_ARRAY )
			{
				// get the value into buffer first - AVOIDING PREFIX BUGS
				arrTemp = DynArrayItem( arrRecord, dwColumn );
				if ( arrTemp == NULL )
					return;

				// form the array of values into one single string with ',' seperated
				__PrepareString( arrTemp, pszValue, pColumn->dwWidth, szFormat, szArraySeperator );
			}
			else
			{
				// get the value using format specified
				FORMAT_STRING( pszValue, szFormat, DynArrayItemAsFloat( arrRecord, dwColumn ) );
			}

			// switch case completed
			break;
		}

	case SR_TYPE_DOUBLE:
		{
			// identify the format to be used
			// NOTE: for this type, format needs to be specified
			// if not, value displayed is unpredictable
			if ( pColumn->dwFlags & SR_VALUEFORMAT )
				lstrcpy( szFormat, pColumn->szFormat );
			else
				lstrcpy( szFormat, _T( "%f" ) );		// default format

			// copy the value to the temporary buffer based on the flags specified
			if ( pColumn->dwFlags & SR_ARRAY )
			{
				// get the value into buffer first - AVOIDING PREFIX BUGS
				arrTemp = DynArrayItem( arrRecord, dwColumn );
				if ( arrTemp == NULL )
					return;

				// form the array of values into one single string with ',' seperated
				__PrepareString( arrTemp, pszValue, pColumn->dwWidth, szFormat, szArraySeperator );
			}
			else
			{
				// get the value using format specified
				FORMAT_STRING( pszValue, szFormat, DynArrayItemAsDouble( arrRecord, dwColumn ) );
			}

			// switch case completed
			break;
		}

	case SR_TYPE_TIME:
		{
			// get the time in the required format
			systime = DynArrayItemAsSystemTime( arrRecord, dwColumn );

			// get the time in current locale format
			dwReturn = GetTimeFormat( LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, 
				&systime, NULL, pszValue, MAX_STRING_LENGTH );

			// check the result
			if ( dwReturn == 0 )
			{
				// save the error info that has occurred
				SaveLastError();
				lstrcpy( pszValue, GetReason() );
			}

			// switch case completed
			break;
		}

	case SR_TYPE_CUSTOM:
		{
			// check whether function pointer is specified or not
			// if not specified, error
			if ( pColumn->pFunction == NULL )
				return;			// function ptr not specified ... error

			// determine the data to be passed to the formatter function
			pData = pColumn->pFunctionData;
			if ( pData == NULL ) // function data is not defined
				pData = pColumn;		// the current column info itself as data

			// call the custom function
			( *pColumn->pFunction)( dwColumn, arrRecord, pData, pszValue );

			// switch case completed
			break;
		}

	case SR_TYPE_DATE:
	case SR_TYPE_DATETIME:
	default:
		{
			// this should not occur ... still
			lstrcpy( pszValue, NULL_STRING );

			// switch case completed
			break;
		}
	}

	// user wants to display "N/A", when the value is empty, copy that
	if ( lstrlen( pszValue ) == 0 && pColumn->dwFlags & SR_SHOW_NA_WHEN_BLANK )
	{
		// copy N/A
		lstrcpy( pszValue, V_NOT_AVAILABLE );
	}
}

// ***************************************************************************
// Routine Description:
//		  
// Arguments:
//
// Return Value:
// 
// ***************************************************************************
VOID __DisplayTextWrapped( FILE* fp, LPTSTR pszValue, LPCTSTR pszSeperator, DWORD dwWidth )
{
	// local variables
	LPTSTR pszBuffer = NULL;
	LPCTSTR pszRestValue = NULL;
	DWORD dwTemp = 0, dwLength = 0, dwSepLength = 0;

	// check the input
	if ( pszValue == NULL || dwWidth == 0 || fp == NULL )
		return;

	// allocate buffer
	dwLength = StringLengthInBytes( pszValue );
	if ( dwLength < dwWidth )
		dwLength = dwWidth;

	// ...
	pszBuffer = __calloc( dwLength + 5, sizeof( TCHAR ) );
	if ( pszBuffer == NULL )
	{
		SetLastError( E_OUTOFMEMORY );
		SaveLastError();
		lstrcpy( pszValue, NULL_STRING );			// null-ify the remaining text
		return;
	}

	// determine the length of the seperator
	dwSepLength = 0;
	if ( pszSeperator != NULL )
		dwSepLength = StringLengthInBytes( pszSeperator );

	// determine the length of the data that can be displayed in this row
	dwTemp = 0;
	dwLength = 0;
	while ( 1 )
	{
		pszRestValue = NULL;
		if ( pszSeperator != NULL )
			pszRestValue = FindString( pszValue, pszSeperator, dwLength );

		// check whether seperator is found or not
		if ( pszRestValue != NULL )
		{
			// determine the position
			dwTemp = StringLengthInBytes( pszValue ) - StringLengthInBytes( pszRestValue ) + dwSepLength;

			// check the length
			if ( dwTemp >= dwWidth )
			{
				// string position exceed the max. width
				if ( dwLength == 0 || dwTemp == dwWidth )
					dwLength = dwWidth;

				// break from the loop
				break;
			}

			// store the current position
			dwLength = dwTemp;
		}
		else
		{
			// check if length is determined or not .. if not required width itself is length
			if ( dwLength == 0 || ((StringLengthInBytes( pszValue ) - dwLength) > dwWidth) )
 				dwLength = dwWidth;
			else if ( StringLengthInBytes( pszValue ) <= (LONG) dwWidth )
				dwLength = StringLengthInBytes( pszValue );
	
			// break the loop
			break;
		}
	}

	// get the partial value that has to be displayed
	lstrcpyn( pszBuffer, pszValue, dwLength + 1 );			// +1 for NULL character
	AdjustStringLength( pszBuffer, dwWidth, FALSE );		// adjust the string
	ShowMessage( fp, pszBuffer );							// display the value

	// change the buffer contents so that it contains rest of the undisplayed text
	lstrcpy( pszBuffer, pszValue );
	if ( StringLengthInBytes( pszValue ) > (LONG) dwLength )
		lstrcpy( pszValue, pszBuffer + dwLength );
	else
		lstrcpy( pszValue, _T( "" ) );

	// release the memory allocated
	__free( pszBuffer );
}

// ***************************************************************************
// Routine Description:
//		Displays the arrData in Tabular form.
//		  
// Arguments:
//      [ in ] fp			: Output Device
//      [ in ] dwColumns	: no. of columns
//      [ in ] pColumns		: Header strings
//      [ in ] dwFlags		: flags
//      [ in ] arrData		: data to be shown
//
// Return Value:
//      NONE
// 
// ***************************************************************************
VOID __ShowAsTable( FILE* fp, DWORD dwColumns, 
				    PTCOLUMNS pColumns, DWORD dwFlags, TARRAY arrData )
{
	// local variables
	DWORD dwCount = 0;							// holds the count of the records
	DWORD i = 0, j = 0, k = 0;					// looping variables
	DWORD dwColumn = 0;
	LONG lLastColumn = 0;
	DWORD dwMultiLineColumns = 0;
	BOOL bNeedSpace = FALSE;
	BOOL bPadLeft = FALSE;
	TARRAY arrRecord = NULL;
	TARRAY arrMultiLine = NULL;
	LPCTSTR pszData = NULL;
	LPCTSTR pszSeperator = NULL;
	__STRING_4096 szValue = NULL_STRING;	// custom value formatter

	// constants
	const DWORD cdwColumn = 0;
	const DWORD cdwSeperator = 1;
	const DWORD cdwData = 2;

	// create an multi-line data display helper array
	arrMultiLine = CreateDynamicArray();
	if ( arrMultiLine == NULL )
	{
		SetLastError( E_OUTOFMEMORY );
		SaveLastError();
		return;
	}

	// check whether header has to be displayed or not
	if ( ! ( dwFlags & SR_NOHEADER ) )
	{
		// 
		// header needs to be displayed

		// traverse thru the column headers and display
		bNeedSpace = FALSE;
		for ( i = 0; i < dwColumns; i++ )
		{
			//	check whether user wants to display this column or not
			if ( pColumns[ i ].dwFlags & SR_HIDECOLUMN ) 
				continue;		// user doesn't want this column to be displayed .. skip

			// determine the padding direction
			bPadLeft = FALSE;
			if ( pColumns[ i ].dwFlags & SR_ALIGN_LEFT )
				bPadLeft = TRUE;
			else 
			{ 
				switch( pColumns[ i ].dwFlags & SR_TYPE_MASK )
				{
				case SR_TYPE_NUMERIC:
				case SR_TYPE_FLOAT:
				case SR_TYPE_DOUBLE:
					bPadLeft = TRUE;
					break;
				}
			}

			// check if column header seperator is needed or not and based on that show
			if ( bNeedSpace == TRUE )
			{
				// show space as column header seperator
				DISPLAY_MESSAGE( fp, _T( " " ) );
			}

			// print the column heading
			// NOTE: column will be displayed either by expanding or shrinking
			//		 based on the length of the column heading as well as width of the column
			FORMAT_STRING_EX( szValue, _T( "%s" ), 
				pColumns[ i ].szColumn, pColumns[ i ].dwWidth, bPadLeft );
			DISPLAY_MESSAGE( fp, szValue );	// column heading

			// inform that from next time onward display column header separator
			bNeedSpace = TRUE;
		}

		// display the new line character ... seperation b/w headings and separator line
		DISPLAY_MESSAGE( fp, _T( "\n" ) );
	
		//	display the seperator chars under each column header
		bNeedSpace = FALSE;
		for ( i = 0; i < dwColumns; i++ )
		{
			//	check whether user wants to display this column or not
			if ( pColumns[ i ].dwFlags & SR_HIDECOLUMN ) 
				continue;		// user doesn't want this column to be displayed .. skip
			
			// check if column header seperator is needed or not and based on that show
			if ( bNeedSpace == TRUE )
			{
				// show space as column header seperator
				DISPLAY_MESSAGE( fp, _T( " " ) );
			}

			//	display seperator based on the required column width
			DISPLAY_MESSAGE( fp, Replicate( szValue, _T( "=" ), pColumns[ i ].dwWidth ) );

			// inform that from next time onward display column header separator
			bNeedSpace = TRUE;
		}

		// display the new line character ... seperation b/w headings and actual data
		DISPLAY_MESSAGE( fp, _T( "\n" ) );
	}
	
	//
	// start displaying

	// get the total no. of records available
	dwCount = DynArrayGetCount( arrData );

	// traverse thru the records one-by-one
	for( i = 0; i < dwCount; i++ )
	{
		// clear the existing value
		lstrcpy( szValue, NULL_STRING );

		// get the pointer to the current record
		arrRecord = DynArrayItem( arrData, i );
		if ( arrRecord == NULL )
			continue;

		// traverse thru the columns and display the values
		bNeedSpace = FALSE;
		for ( j = 0; j < dwColumns; j++ )
		{
			// sub-local variables used in this loop
			DWORD dwTempWidth = 0;
			BOOL bTruncation = FALSE;

			//	check whether user wants to display this column or not
			if ( pColumns[ j ].dwFlags & SR_HIDECOLUMN ) 
				continue;		// user doesn't want this column to be displayed .. skip
			
			// get the value of the column
			// NOTE: CHECK IF USER ASKED NOT TO TRUNCATE THE DATA OR NOT
			if ( pColumns[ j ].dwFlags & SR_NO_TRUNCATION )
			{
				bTruncation = TRUE;
				dwTempWidth = pColumns[ j ].dwWidth;
				pColumns[ j ].dwWidth = SIZE_OF_ARRAY( szValue );
			}

			// prepare the value
			__GetValue( &pColumns[ j ], j, arrRecord, szValue, _T( ", " ) );

			// determine the padding direction
			bPadLeft = FALSE;
			if ( bTruncation == FALSE )
			{
				if ( pColumns[ j ].dwFlags & SR_ALIGN_LEFT )
					bPadLeft = TRUE;
				else 
				{ 
					switch( pColumns[ j ].dwFlags & SR_TYPE_MASK )
					{
					case SR_TYPE_NUMERIC:
					case SR_TYPE_FLOAT:
					case SR_TYPE_DOUBLE:
						bPadLeft = TRUE;
						break;
					}
				}

				// adjust ...
				AdjustStringLength( szValue, pColumns[ j ].dwWidth, bPadLeft );
			}
			
			// reset the width of the current column if it is modified
			if ( bTruncation == TRUE )
				pColumns[ j ].dwWidth = dwTempWidth;

			// check if column header seperator is needed or not and based on that show
			if ( bNeedSpace == TRUE )
			{
				// show space as column header seperator
				DISPLAY_MESSAGE( fp, _T( " " ) );
			}

			// now display the value
			if ( pColumns[ j ].dwFlags & SR_WORDWRAP )
			{
				// display the text ( might be partial )
				__DisplayTextWrapped( fp, szValue, _T( ", " ), pColumns[ j ].dwWidth );

				// check if any info is left to be displayed
				if ( StringLengthInBytes( szValue ) != 0 )
				{
					LONG lIndex = 0;
					lIndex = DynArrayAppendRow( arrMultiLine, 3 );
					if ( lIndex != -1 )
					{
						DynArraySetDWORD2( arrMultiLine, lIndex, cdwColumn, j );
						DynArraySetString2( arrMultiLine, lIndex, cdwData, szValue, 0 );
						DynArraySetString2( arrMultiLine, lIndex, cdwSeperator, _T( ", " ), 0 );
					}
				}
			}
			else
			{
				DISPLAY_MESSAGE( fp, szValue );
			}

			// inform that from next time onward display column header separator
			bNeedSpace = TRUE;
		}

		// display the new line character ... seperation b/w two record
		DISPLAY_MESSAGE( fp, _T( "\n" ) );

		// now display the multi-line column values
		dwMultiLineColumns = DynArrayGetCount( arrMultiLine );
		while( dwMultiLineColumns != 0 )
		{
			// reset
			dwColumn = 0;
			lLastColumn = -1;
			bNeedSpace = FALSE;

			// ...
			for( j = 0; j < dwMultiLineColumns; j++ )
			{
				// ge the column number
				dwColumn = DynArrayItemAsDWORD2( arrMultiLine, j, cdwColumn );

				// show spaces till the current column from the last column
				for( k = lLastColumn + 1; k < dwColumn; k++ )
				{
					//	check whether user wants to display this column or not
					if ( pColumns[ k ].dwFlags & SR_HIDECOLUMN ) 
						continue;		// user doesn't want this column to be displayed .. skip
					
					// check if column header seperator is needed or not and based on that show
					if ( bNeedSpace == TRUE )
					{
						// show space as column header seperator
						DISPLAY_MESSAGE( fp, _T( " " ) );
					}

					//	display seperator based on the required column width
					DISPLAY_MESSAGE( fp, Replicate( szValue, _T( " " ), pColumns[ k ].dwWidth ) );

					// inform that from next time onward display column header separator
					bNeedSpace = TRUE;
				}

				// update the last column
				lLastColumn = dwColumn;

				// check if column header seperator is needed or not and based on that show
				if ( bNeedSpace == TRUE )
				{
					// show space as column header seperator
					DISPLAY_MESSAGE( fp, _T( " " ) );
				}

				// get the seperator and data
				pszData = DynArrayItemAsString2( arrMultiLine, j, cdwData );
				pszSeperator = DynArrayItemAsString2( arrMultiLine, j, cdwSeperator );
				if ( pszData == NULL || pszSeperator == NULL )
					continue;

				// display the information
				lstrcpyn( szValue, pszData, SIZE_OF_ARRAY( szValue ) );
				__DisplayTextWrapped( fp, szValue, pszSeperator, pColumns[ dwColumn ].dwWidth );

				// update the multi-line array with rest of the line
				if ( StringLengthInBytes( szValue ) == 0 )
				{
					// data in this column is completely displayed ... remove it
					DynArrayRemove( arrMultiLine, j );

					// update the indexes
					j--;
					dwMultiLineColumns--;
				}
				else
				{
					// update the multi-line array with the remaining value
					DynArraySetString2( arrMultiLine, j, cdwData, szValue, 0 );
				}
			}

			// display the new line character ... seperation b/w two lines
			DISPLAY_MESSAGE( fp, _T( "\n" ) );
		}
	}

	// destroy the array
	DestroyDynamicArray( &arrMultiLine );
}

// ***************************************************************************
// Routine Description:
//		Displays the  in List format
//		  
// Arguments:
//      [ in ] fp			: Output Device
//      [ in ] dwColumns	: no. of columns
//      [ in ] pColumns		: Header strings
//      [ in ] dwFlags		: flags
//      [ in ] arrData		: data to be shown
//
// Return Value:
//      NONE
// 
// ***************************************************************************
VOID __ShowAsList( FILE* fp, DWORD dwColumns, 
				   PTCOLUMNS pColumns, DWORD dwFlags, TARRAY arrData )
{
	// local variables
	DWORD dwCount = 0;			// holds the count of all records
	DWORD i  = 0, j = 0;		// looping variables
	DWORD dwTempWidth = 0;
	DWORD dwMaxColumnLen = 0;	// holds the length of the which max. among all the columns
	LPTSTR pszTemp = NULL;
	TARRAY arrRecord = NULL;
	__STRING_64 szBuffer = NULL_STRING;
	__STRING_4096 szValue = NULL_STRING;	// custom value formatter

	// find out the max. length among all the column headers
	dwMaxColumnLen = 0;
	for ( i = 0; i < dwColumns; i++ )
	{
		if ( dwMaxColumnLen < ( DWORD ) StringLengthInBytes( pColumns[ i ].szColumn ) )
			dwMaxColumnLen = StringLengthInBytes( pColumns[ i ].szColumn );
	}
	
	//
	// start displaying the data

	// get the total no. of records available
	dwCount = DynArrayGetCount( arrData );

	// get the total no. of records available
	for( i = 0; i < dwCount; i++ )
	{
		// get the pointer to the current record
		arrRecord = DynArrayItem( arrData, i );
		if ( arrRecord == NULL )
			continue;

		// traverse thru the columns and display the values
		for ( j = 0; j < dwColumns; j++)
		{
			// clear the existing value
			lstrcpy( szValue, NULL_STRING );

			//	check whether user wants to display this column or not
			if ( pColumns[ j ].dwFlags & SR_HIDECOLUMN ) 
				continue;		// user doesn't want this column to be displayed .. skip

			// display the column heading and its value
			// ( heading will be displayed based on the max. column length )
			FORMAT_STRING_EX( szValue, _T( "%s:" ), 
				pColumns[ j ].szColumn, dwMaxColumnLen + 2, FALSE );
			DISPLAY_MESSAGE( fp, szValue );
			
			// get the value of the column
			dwTempWidth = pColumns[ j ].dwWidth;				// save the current width
			pColumns[ j ].dwWidth = SIZE_OF_ARRAY( szValue );	// change the width
			__GetValue( &pColumns[ j ], j, arrRecord, szValue, _T( "\n" ) );
			pColumns[ j ].dwWidth = dwTempWidth;		// restore the original width

			// display the [ list of ] values
			pszTemp = _tcstok( szValue, _T( "\n" ) );
			while ( pszTemp != NULL )
			{
				// display the value
				DISPLAY_MESSAGE( fp, pszTemp );
				pszTemp = _tcstok( NULL, _T( "\n" ) );
				if ( pszTemp != NULL )
				{
					// prepare the next line
					FORMAT_STRING_EX( szBuffer, _T( "%s" ), _T( " " ), 
						dwMaxColumnLen + 2, FALSE );
					DISPLAY_MESSAGE( fp, _T( "\n" ) );
					DISPLAY_MESSAGE( fp, szBuffer );
				}
			}

			// display the next line character seperation b/w two fields
			DISPLAY_MESSAGE( fp, _T( "\n" ) );
		}

		// display the new line character ... seperation b/w two records
		// NOTE: do this only if there are some more records
		if ( i + 1 < dwCount )
		{
			DISPLAY_MESSAGE( fp, _T( "\n" ) );
		}
	}
}

// ***************************************************************************
// Routine Description:
//     Displays the arrData in CSV form.
//		  
// Arguments:
//      [ in ] fp			: Output Device
//      [ in ] dwColumns	: no. of columns
//      [ in ] pColumns		: Header strings
//      [ in ] dwFlags		: flags
//      [ in ] arrData		: data to be shown
//
// Return Value:
//      NONE
// 
// ***************************************************************************
VOID __ShowAsCSV( FILE* fp, DWORD dwColumns, 
				  PTCOLUMNS pColumns, DWORD dwFlags, TARRAY arrData )
{
	// local variables
	DWORD dwCount = 0;			// holds the count of all records
	DWORD i  = 0, j = 0;		// looping variables
	DWORD dwTempWidth = 0;
	BOOL bNeedComma = FALSE;
	TARRAY arrRecord = NULL;
	__STRING_4096 szValue = NULL_STRING;

	// check whether header has to be displayed or not
	if ( ! ( dwFlags & SR_NOHEADER ) )
	{
		// 
		// header needs to be displayed

		// first display the columns ... with comma seperated
		bNeedComma = FALSE;
		for ( i = 0; i < dwColumns; i++ )
		{
			//	check whether user wants to display this column or not
			if ( pColumns[ i ].dwFlags & SR_HIDECOLUMN ) 
				continue;		// user doesn't want this column to be displayed .. skip

			// check whether we need to display ',' or not and then display
			if ( bNeedComma == TRUE )
			{
				// ',' has to be displayed
				DISPLAY_MESSAGE( fp, _T( "," ) );
			}

			// display the column heading
			DISPLAY_MESSAGE1( fp, szValue, _T( "\"%s\"" ), pColumns[ i ].szColumn );

			// inform that from next time onwards we need to display comma before data
			bNeedComma = TRUE;
		}

		// new line character
		DISPLAY_MESSAGE( fp, _T( "\n" ) );
	}
	
	//
	// start displaying the data

	// get the total no. of records available
	dwCount = DynArrayGetCount( arrData );

	// get the total no. of records available
	for( i = 0; i < dwCount; i++ )
	{
		// get the pointer to the current record
		arrRecord = DynArrayItem( arrData, i );
		if ( arrRecord == NULL )
			continue;

		// traverse thru the columns and display the values
		bNeedComma = FALSE;
		for ( j = 0; j < dwColumns; j++ )
		{
			// clear the existing value
			lstrcpy( szValue, NULL_STRING );

			//	check whether user wants to display this column or not
			if ( pColumns[ j ].dwFlags & SR_HIDECOLUMN ) 
				continue;		// user doesn't want this column to be displayed .. skip

			// get the value of the column
			dwTempWidth = pColumns[ j ].dwWidth;			// save the current width
			pColumns[ j ].dwWidth = SIZE_OF_ARRAY( szValue );		// change the width
			__GetValue( &pColumns[ j ], j, arrRecord, szValue, _T( "," ) );
			pColumns[ j ].dwWidth = dwTempWidth;		// restore the original width

			// check whether we need to display ',' or not and then display
			if ( bNeedComma == TRUE )
			{
				// ',' has to be displayed
				DISPLAY_MESSAGE( fp, _T( "," ) );
			}

			// print the value
			DISPLAY_MESSAGE( fp, _T( "\"" ) );
			DISPLAY_MESSAGE( fp, szValue );
			DISPLAY_MESSAGE( fp, _T( "\"" ) );

			// inform that from next time onwards we need to display comma before data
			bNeedComma = TRUE;
		}

		// new line character
		DISPLAY_MESSAGE( fp, _T( "\n" ) );
	}
}

//
// public functions ... exposed to external world
//

// ***************************************************************************
// Routine Description:
//		  
// Arguments:
//
// Return Value:
// 
// ***************************************************************************
VOID ShowResults( DWORD dwColumns, PTCOLUMNS pColumns, DWORD dwFlags, TARRAY arrData )
{
	// just call the main function ... with stdout
	ShowResults2( stdout, dwColumns, pColumns, dwFlags, arrData );
}

// ***************************************************************************
// Routine Description:
//      Show the resuls (arrData) on the screen.
//		  
// Arguments:
//      [ in ] dwColumns	: No. of Columns to be shown
//      [ in ] pColumns		: Columns header
//      [ in ] dwFlags		: Required format
//      [ in ] arrData		: Data to be displayed.
//
// Return Value:
//      NONE
// 
// ***************************************************************************
VOID ShowResults2( FILE* fp, DWORD dwColumns, PTCOLUMNS pColumns, DWORD dwFlags, TARRAY arrData )
{
	// local variables
	
	//
	//	Display the information in the format specified
	//
	switch( dwFlags & SR_FORMAT_MASK )
	{
	case SR_FORMAT_TABLE:
		{
			// show the data in table format
			__ShowAsTable( fp, dwColumns, pColumns, dwFlags, arrData );

			// switch case completed
			break;
		}

	case SR_FORMAT_LIST:
		{
			// show the data in table format
			__ShowAsList( fp, dwColumns, pColumns, dwFlags, arrData );

			// switch case completed
			break;
		}

	case SR_FORMAT_CSV:
		{
			// show the data in table format
			__ShowAsCSV( fp, dwColumns, pColumns, dwFlags, arrData );

			// switch case completed
			break;
		}

	default:
		{
			// invalid format requested by the user
			break;
		}
	}

	// flush the memory onto the file buffer
	fflush( fp );
}