//
//    This file is part of Dire Wolf, an amateur radio packet TNC.
//
//    Copyright (C) 2013, 2015  John Langner, WB2OSZ
//
//    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, see <http://www.gnu.org/licenses/>.
//

/*------------------------------------------------------------------
 *
 * Module:      tt_text.c
 *
 * Purpose:   	Translate between text and touch tone representation.
 *		
 * Description: Letters can be represented by different touch tone
 *		keypad sequences.
 *
 * References:	This is based upon APRStt (TM) documents but not 100%
 *		compliant due to ambiguities and inconsistencies in
 *		the specifications.
 *
 *		http://www.aprs.org/aprstt.html
 *
 *---------------------------------------------------------------*/

/*
 * There are two different encodings called:
 *
 *   * Two-key
 *
 *		Digits are represented by a single key press.
 *		Letters (or space) are represented by the corresponding
 *		key followed by A, B, C, or D depending on the position
 *		of the letter.
 *
 *   * Multi-press
 *
 *		Letters are represented by one or more key presses
 *		depending on their position.
 *		e.g. on 5/JKL key, J = 1 press, K = 2, etc. 
 *		The digit is the number of letters plus 1.
 *		In this case, press 5 key four times to get digit 5.
 *		When two characters in a row use the same key,
 *		use the "A" key as a separator.
 *
 * Examples:
 *
 *	Character	Multipress	Two Key		Comments
 *	---------	----------	-------		--------
 *	0		00		0		Space is handled like a letter.
 *	1		1		1		No letters on 1 button.
 *	2		2222		2		3 letters -> 4 key presses
 *	9		99999		9
 *	W		9		9A
 *	X		99		9B
 *	Y		999		9C
 *	Z		9999		9D
 *	space		0		0A		0A was used in an APRStt comment example.
 *
 *
 * Note that letters can occur in callsigns and comments.
 * Everywhere else they are simply digits.
 *
 *
 *   * New fixed length callsign format
 *
 *
 * 	The "QIKcom-2" project adds a new format where callsigns are represented by
 * 	a fixed length string of only digits.  The first 6 digits are the buttons corresponding
 * 	to the letters.  The last 4 take a little calculation.  Example:
 *
 *		W B 4 A P R	original.
 *		9 2 4 2 7 7	corresponding button.
 *		1 2 0 1 1 2	character position on key.  0 for the digit.
 *
 * 	Treat the last line as a base 4 number.
 * 	Convert it to base 10 and we get 1558 for the last four digits.
 */

/*
 * Everything is based on this table.
 * Changing it will change everything.
 * In other words, don't mess with it.  
 * The world will come crumbling down.
 */

static const char translate[10][4] = {
		/*	 A	 B	 C	 D  */
		/*	---	---	---	--- */
	/* 0 */	{	' ',	 0,	 0,	 0  },
	/* 1 */	{	 0,	 0,	 0,	 0  },
	/* 2 */	{	'A',	'B',	'C',	 0  },
	/* 3 */	{	'D',	'E',	'F',	 0  },
	/* 4 */	{	'G',	'H',	'I',	 0  },
	/* 5 */	{	'J',	'K',	'L',	 0  },
	/* 6 */	{	'M',	'N',	'O',	 0  },
	/* 7 */	{	'P',	'Q',	'R',	'S' },
	/* 8 */	{	'T',	'U',	'V',	 0  },
	/* 9 */	{	'W',	'X',	'Y',	'Z' } };


/*
 * This is for the new 10 character fixed length callsigns for APRStt 3.
 * Notice that it uses an old keypad layout with Q & Z on the 1 button.
 * The TH-D72A and all telephones that I could find all have 
 * four letters each on the 7 and 9 buttons.
 * This inconsistency is sure to cause confusion but the 6+4 scheme won't
 * be possible with more than 4 characters assigned to one button.
 * 4**6-1 = 4096 which fits in 4 decimal digits.
 * 5**6-1 = 15624 would not fit.
 *
 * The column is a two bit code packed into the last 4 digits.
 */

static const char call10encoding[10][4] = {
		/*	 0	 1	 2	 3  */
		/*	---	---	---	--- */
	/* 0 */	{	'0',	' ',	 0,	 0   },
	/* 1 */	{	'1',	'Q',	'Z',	 0   },
	/* 2 */	{	'2',	'A',	'B',	'C'  },
	/* 3 */	{	'3',	'D',	'E',	'F'  },
	/* 4 */	{	'4',	'G',	'H',	'I'  },
	/* 5 */	{	'5',	'J',	'K',	'L'  },
	/* 6 */	{	'6',	'M',	'N',	'O'  },
	/* 7 */	{	'7',	'P',	'R',	'S'  },
	/* 8 */	{	'8',	'T',	'U',	'V'  },
	/* 9 */	{	'9',	'W',	'X',	'Y'  } };


/*
 * Special satellite 4 digit gridsquares to cover "99.99% of the world's population."
 */

static const char grid[10][10][3] =      
     {  { "AP", "BP", "AO", "BO", "CO", "DO", "EO", "FO", "GO", "OJ" },		// 0 - Canada
        { "CN", "DN", "EN", "FN", "GN", "CM", "DM", "EM", "FM", "OI" },		// 1 - USA
        { "DL", "EL", "FL", "DK", "EK", "FK", "EJ", "FJ", "GJ", "PI" },		// 2 - C. America
        { "FI", "GI", "HI", "FH", "GH", "HH", "FG", "GG", "FF", "GF" },		// 3 - S. America
        { "JP", "IO", "JO", "KO", "IN", "JN", "KN", "IM", "JM", "KM" },		// 4 - Europe
        { "LO", "MO", "NO", "OO", "PO", "QO", "RO", "LN", "MN", "NN" },		// 5 - Russia
        { "ON", "PN", "QN", "OM", "PM", "QM", "OL", "PL", "OK", "PK" },		// 6 - Japan, China
        { "LM", "MM", "NM", "LL", "ML", "NL", "LK", "MK", "NK", "LJ" },		// 7 - India
        { "PH", "QH", "OG", "PG", "QG", "OF", "PF", "QF", "RF", "RE" },		// 8 - Aus / NZ
        { "IL", "IK", "IJ", "JJ", "JI", "JH", "JG", "KG", "JF", "KF" }  };	// 9 - Africa

#include "direwolf.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdarg.h>

#include "textcolor.h"
#include "tt_text.h"


#if defined(ENC_MAIN) || defined(DEC_MAIN)

void text_color_set (dw_color_t c) { return; }

int dw_printf (const char *fmt, ...) 
{
	va_list args;
	int len;
	
	va_start (args, fmt);
	len = vprintf (fmt, args);
	va_end (args);
	return (len);
}

#endif


/*------------------------------------------------------------------
 *
 * Name:        tt_text_to_multipress
 *
 * Purpose:     Convert text to the multi-press representation.
 *
 * Inputs:      text	- Input string.
 *			  Should contain only digits, letters, or space.
 *			  All other punctuation is treated as space.
 *
 *		quiet	- True to suppress error messages.
 *	
 * Outputs:	buttons	- Sequence of buttons to press.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_text_to_multipress (const char *text, int quiet, char *buttons)
{
	const char *t = text;
	char *b = buttons;
	char c;
	int row, col;
	int errors = 0;
	int found;
	int n;

	*b = '\0';
	
	while ((c = *t++) != '\0') {

	  if (isdigit(c)) {
	
/* Count number of other characters assigned to this button. */
/* Press that number plus one more. */

	    n = 1;
	    row = c - '0';
	    for (col=0; col<4; col++) {
	      if (translate[row][col] != 0) {
	        n++;
	      }
	    }
	    if (buttons[0] != '\0' && *(b-1) == row + '0') {
	      *b++ = 'A';
	    }
	    while (n--) {
	      *b++ = row + '0';
	      *b = '\0';
	    }
	  }
	  else {
	    if (isupper(c)) {
	      ;
	    }	  
	    else if (islower(c)) {
	      c = toupper(c);
	    }
	    else if (c != ' ') {
	      errors++;
	      if (! quiet) {
	        text_color_set (DW_COLOR_ERROR);
		dw_printf ("Text to multi-press: Only letters, digits, and space allowed.\n");
	      }
	      c = ' ';
	    }

/* Search for everything else in the translation table. */
/* Press number of times depending on column where found. */

	    found = 0;

	    for (row=0; row<10 && ! found; row++) {
	      for (col=0; col<4 && ! found; col++) {
	        if (c == translate[row][col]) {

/* Stick in 'A' if previous character used same button. */

	          if (buttons[0] != '\0' && *(b-1) == row + '0') {
	            *b++ = 'A';
	          }
	          n = col + 1;
	          while (n--) {
	            *b++ = row + '0';
	            *b = '\0';
	            found = 1;
	          }
	        }
	      }
	    }
	    if (! found) {
	      errors++;
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Text to multi-press: INTERNAL ERROR.  Should not be here.\n");
	    }
	  }
	}
	return (errors);          

} /* end tt_text_to_multipress */


/*------------------------------------------------------------------
 *
 * Name:        tt_text_to_two_key
 *
 * Purpose:     Convert text to the two-key representation.
 *
 * Inputs:      text	- Input string.
 *			  Should contain only digits, letters, or space.
 *			  All other punctuation is treated as space.
 *
 *		quiet	- True to suppress error messages.
 *	
 * Outputs:	buttons	- Sequence of buttons to press.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_text_to_two_key (const char *text, int quiet, char *buttons)
{
	const char *t = text;
	char *b = buttons;
	char c;
	int row, col;
	int errors = 0;
	int found;


	*b = '\0';
	
	while ((c = *t++) != '\0') {

	  if (isdigit(c)) {
	
/* Digit is single key press. */
	  
	    *b++ = c;
	    *b = '\0';
	  }
	  else {
	    if (isupper(c)) {
	      ;
	    }	  
	    else if (islower(c)) {
	      c = toupper(c);
	    }
	    else if (c != ' ') {
	      errors++;
	      if (! quiet) {
	        text_color_set (DW_COLOR_ERROR);
		dw_printf ("Text to two key: Only letters, digits, and space allowed.\n");
	      }
	      c = ' ';
	    }

/* Search for everything else in the translation table. */

	    found = 0;

	    for (row=0; row<10 && ! found; row++) {
	      for (col=0; col<4 && ! found; col++) {
	        if (c == translate[row][col]) {
		  *b++ = '0' + row;
	          *b++ = 'A' + col;
	          *b = '\0';
	          found = 1;
	        }
	      }
	    }
	    if (! found) {
	      errors++;
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Text to two-key: INTERNAL ERROR.  Should not be here.\n");
	    }
	  }
	}
	return (errors);          

} /* end tt_text_to_two_key */


/*------------------------------------------------------------------
 *
 * Name:        tt_letter_to_two_digits
 *
 * Purpose:     Convert one letter to 2 digit representation.
 *
 * Inputs:      c	- One letter.
 *
 *		quiet	- True to suppress error messages.
 *
 * Outputs:	buttons	- Sequence of two buttons to press.
 *			  "00" for error because this is probably
 *			  being used to build up a fixed length
 *			  string where positions are signficant.
 *			  Must be at least 3 bytes.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/


// TODO:  need to test this.

int tt_letter_to_two_digits (char c, int quiet, char buttons[3])
{
	int row, col;
	int errors = 0;
	int found;

	strlcpy(buttons, "", 3);
  
	if (islower(c)) {
	  c = toupper(c);
	}

	if ( ! isupper(c)) {
	  errors++;
	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Letter to two digits: \"%c\" found where a letter is required.\n", c);
	  }
	  strlcpy (buttons, "00", 3);
	  return (errors);
	}

/* Search in the translation table. */

	found = 0;

	for (row=0; row<10 && ! found; row++) {
	  for (col=0; col<4 && ! found; col++) {
	    if (c == translate[row][col]) {
	      buttons[0] = '0' + row;
	      buttons[1] = '1' + col;
	      buttons[2] = '\0';
	      found = 1;
	    }
	  }
	 }
	 if (! found) {
	  errors++;
	  text_color_set (DW_COLOR_ERROR);
	  dw_printf ("Letter to two digits: INTERNAL ERROR.  Should not be here.\n");
	  strlcpy (buttons, "00", 3);
	}

	return (errors);          

} /* end tt_letter_to_two_digits */


/*------------------------------------------------------------------
 *
 * Name:        tt_text_to_call10
 *
 * Purpose:     Convert text to the 10 character callsign format.
 *
 * Inputs:      text	- Input string.
 *			  Should contain from 1 to 6 letters and digits.
 *
 *		quiet	- True to suppress error messages.
 *	
 * Outputs:	buttons	- Sequence of buttons to press.
 *			  Should be exactly 10 unless error.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_text_to_call10 (const char *text, int quiet, char *buttons)
{
	const char *t;
	char *b;
	char c;
	int packed;		/* two bits per character */
	int row, col;
	int errors = 0;
	int found;
	char padded[8];
	char stemp[11];


	strcpy (buttons, "");

/* Quick validity check. */
	
	if (strlen(text) < 1 || strlen(text) > 6) {

	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Text to callsign 6+4: Callsign \"%s\" not between 1 and 6 characters.\n", text);
	  }
	  errors++;
	  return (errors);
   	}

	for (t = text; *t != '\0'; t++) {

	  if (! isalnum(*t)) {
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Text to callsign 6+4: Callsign \"%s\" can contain only letters and digits.\n", text);
	    }
	    errors++;
	    return (errors);
	  }
   	}

/* Append spaces if less than 6 characters. */

	strcpy (padded, text);
	while (strlen(padded) < 6) {
	  strcat (padded, " ");
	}

	b = buttons;
	packed = 0;

	for (t = padded; *t != '\0'; t++) {
	
	  c = *t;
	  if (islower(c)) {
	      c = toupper(c);
	  }

/* Search in the translation table. */

	  found = 0;

	  for (row=0; row<10 && ! found; row++) {
	    for (col=0; col<4 && ! found; col++) {
	      if (c == call10encoding[row][col]) {
	        *b++ = '0' + row;
	        *b = '\0';
	        packed = packed * 4 + col;  /* base 4 to binary */
	        found = 1;
	      }
	    }
	  }

	  if (! found) {
	    /* Earlier check should have caught any character not in translation table. */
	    errors++;
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Text to callsign 6+4: INTERNAL ERROR 0x%02x.  Should not be here.\n", c);
	  }
	}

/* Binary to decimal for the columns. */

	snprintf (stemp, sizeof(stemp), "%04d", packed);
	strcat (buttons, stemp);

	return (errors);          

} /* end tt_text_to_call10 */



/*------------------------------------------------------------------
 *
 * Name:        tt_text_to_satsq		
 *
 * Purpose:     Convert Special Satellite Gridsquare to 4 digit DTMF representation.
 *
 * Inputs:      text	- Input string.
 *			  Should be two letters (A thru R) and two digits.
 *
 *		quiet	- True to suppress error messages.
 *	
 * Outputs:	buttons	- Sequence of buttons to press.
 *			  Should be 4 digits unless error.
 *
 * Returns:     Number of errors detected.
 *
 * Example:	"FM19" is converted to "1819."
 *		"AA00" is converted to empty string and error return code.
 *
 *----------------------------------------------------------------*/

int tt_text_to_satsq (const char *text, int quiet, char *buttons, size_t buttonsize)
{

	int row, col;
	int errors = 0;
	int found;
	char uc[3];


	strlcpy (buttons, "", buttonsize);

/* Quick validity check. */
	
	if (strlen(text) < 1 || strlen(text) > 4) {

	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Satellite Gridsquare to DTMF: Gridsquare \"%s\" must be 4 characters.\n", text);
	  }
	  errors++;
	  return (errors);
   	}

/* Changing to upper case makes things easier later. */

	uc[0] = islower(text[0]) ? toupper(text[0]) : text[0];
	uc[1] = islower(text[1]) ? toupper(text[1]) : text[1];
	uc[2] = '\0';

	if (uc[0] < 'A' || uc[0] > 'R' || uc[1] < 'A' || uc[1] > 'R') {

	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Satellite Gridsquare to DTMF: First two characters \"%s\" must be letters in range of A to R.\n", text);
	  }
	  errors++;
	  return (errors);
	}

	if (! isdigit(text[2]) || ! isdigit(text[3])) {

	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Satellite Gridsquare to DTMF: Last two characters \"%s\" must digits.\n", text);
	  }
	  errors++;
	  return (errors);
   	}


/* Search in the translation table. */

	found = 0;

	for (row=0; row<10 && ! found; row++) {
	  for (col=0; col<10 && ! found; col++) {
	    if (strcmp(uc,grid[row][col]) == 0) {

	      char btemp[8];

	      btemp[0] = row + '0';
	      btemp[1] = col + '0';
	      btemp[2] = text[2];
	      btemp[3] = text[3];
	      btemp[4] = '\0';

	      strlcpy (buttons, btemp, buttonsize);
	      found = 1;
	    }
	  }
	}

	if (! found) {
	  /* Sorry, Greenland, and half of Africa, and ... */
	  errors++;
	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Satellite Gridsquare to DTMF: Sorry, your location can't be converted to DTMF.\n");
	  }
	}

	return (errors);          

} /* end tt_text_to_satsq */



/*------------------------------------------------------------------
 *
 * Name:        tt_text_to_ascii2d
 *
 * Purpose:     Convert text to the two digit per ascii character representation.
 *
 * Inputs:      text	- Input string.
 *			  Any printable ASCII characters.
 *
 *		quiet	- True to suppress error messages.
 *
 * Outputs:	buttons	- Sequence of buttons to press.
 *
 * Returns:     Number of errors detected.
 *
 * Description:	The standard comment format uses the multipress
 *		encoding which allows only single case letters, digits,
 *		and the space character.
 *		This is a more flexible format that can handle all
 *		printable ASCII characters.  We take the character code,
 *		subtract 32 and convert to two decimal digits.  i.e.
 *			space	= 00
 *			!	= 01
 *			"	= 02
 *			...
 *			~	= 94
 *
 *		This is mostly for internal use, so macros can generate
 *		comments with all characters.
 *
 *----------------------------------------------------------------*/

int tt_text_to_ascii2d (const char *text, int quiet, char *buttons)
{
	const char *t = text;
	char *b = buttons;
	char c;
	int errors = 0;


	*b = '\0';

	while ((c = *t++) != '\0') {

	  int n;

	  /* "isprint()" might depend on locale so use brute force. */

	  if (c < ' ' || c > '~') c = '?';

	  n = c - 32;

	  *b++ = (n / 10) + '0';
	  *b++ = (n % 10) + '0';
	  *b = '\0';
	}
	return (errors);

} /* end tt_text_to_ascii2d */






/*------------------------------------------------------------------
 *
 * Name:        tt_multipress_to_text
 *
 * Purpose:     Convert the multi-press representation to text.
 *
 * Inputs:      buttons	- Input string.
 *			  Should contain only 0123456789A.
 *
 *		quiet	- True to suppress error messages.
 *	
 * Outputs:	text	- Converted to letters, digits, space.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_multipress_to_text (const char *buttons, int quiet, char *text)
{
	const char *b = buttons;
	char *t = text;
	char c;
	int row, col;
	int errors = 0;
	int maxspan;
	int n;

	*t = '\0';
	
	while ((c = *b++) != '\0') {

	  if (isdigit(c)) {
	
/* Determine max that can occur in a row. */
/* = number of other characters assigned to this button + 1. */

	    maxspan = 1;
	    row = c - '0';
	    for (col=0; col<4; col++) {
	      if (translate[row][col] != 0) {
	        maxspan++;
	      }
	    }

/* Count number of consecutive same digits. */

	    n = 1;
	    while (c == *b) {
	      b++;
	      n++;
	    }

	    if (n < maxspan) {
	      *t++ = translate[row][n-1];
	      *t = '\0';
	    }
	    else if (n == maxspan) {
	      *t++ = c;
	      *t = '\0';
	    }
	    else {
	      errors++;
	      if (! quiet) {
	        text_color_set (DW_COLOR_ERROR);
	        dw_printf ("Multi-press to text: Maximum of %d \"%c\" can occur in a row.\n", maxspan, c);
	      }
	      /* Treat like the maximum length. */
	      *t++ = c;
	      *t = '\0';
	    }
	  }
	  else if (c == 'A' || c == 'a') {

/* Separator should occur only if digit before and after are the same. */
	     
	    if (b == buttons + 1 || *b == '\0' || *(b-2) != *b) {
	      errors++;
	      if (! quiet) {
	        text_color_set (DW_COLOR_ERROR);
	        dw_printf ("Multi-press to text: \"A\" can occur only between two same digits.\n");
  	      }
	    }
	  }
	  else {

/* Completely unexpected character. */

	    errors++;
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Multi-press to text: \"%c\" not allowed.\n", c);
  	    }
	  }
	}
	return (errors);          

} /* end tt_multipress_to_text */


/*------------------------------------------------------------------
 *
 * Name:        tt_two_key_to_text
 *
 * Purpose:     Convert the two key representation to text.
 *
 * Inputs:      buttons	- Input string.
 *			  Should contain only 0123456789ABCD.
 *
 *		quiet	- True to suppress error messages.
 *	
 * Outputs:	text	- Converted to letters, digits, space.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_two_key_to_text (const char *buttons, int quiet, char *text)
{
	const char *b = buttons;
	char *t = text;
	char c;
	int row, col;
	int errors = 0;

	*t = '\0';
	
	while ((c = *b++) != '\0') {

	  if (isdigit(c)) {
	
/* Letter (or space) if followed by ABCD. */
	    
	    row = c - '0';
	    col = -1;

	    if (*b >= 'A' && *b <= 'D') {
	      col = *b++ - 'A';
	    }
	    else if (*b >= 'a' && *b <= 'd') {
	      col = *b++ - 'a';
	    }

	    if (col >= 0) {
	      if (translate[row][col] != 0) {
	        *t++ = translate[row][col];
	        *t = '\0';
	      }
	      else {
		errors++;
	        if (! quiet) {
	          text_color_set (DW_COLOR_ERROR);
	          dw_printf ("Two key to text: Invalid combination \"%c%c\".\n", c, col+'A');
		}
	      }
	    }
	    else {
	      *t++ = c;
	      *t = '\0';
	    }
	  }
	  else if ((c >= 'A' && c <= 'D') || (c >= 'a' && c <= 'd')) {

/* ABCD not expected here. */
	     
	    errors++;
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Two-key to text: A, B, C, or D in unexpected location.\n");
	    }
	  }
	  else {

/* Completely unexpected character. */

	    errors++;
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Two-key to text: Invalid character \"%c\".\n", c);
  	    }
	  }
	}
	return (errors);          

} /* end tt_two_key_to_text */


/*------------------------------------------------------------------
 *
 * Name:        tt_two_digits_to_letter
 *
 * Purpose:     Convert the two digit representation to one letter.
 *
 * Inputs:      buttons	- Input string.
 *			  Should contain exactly two digits.
 *
 *		quiet	- True to suppress error messages.
 *
 *		textsiz	- Size of result storage.  Typically 2.
 *	
 * Outputs:	text	- Converted to string which should contain one upper case letter.
 *			  Empty string on error.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_two_digits_to_letter (const char *buttons, int quiet, char *text, size_t textsiz)
{
	char c1 = buttons[0];
	char c2 = buttons[1];
	int row, col;
	int errors = 0;
	char stemp2[2];

	strlcpy (text, "", textsiz);
	
	if (c1 >= '2' && c1 <= '9') {

	  if (c2 >= '1' && c2 <= '4') {

	    row = c1 - '0';
	    col = c2 - '1';

	    if (translate[row][col] != 0) {

	      stemp2[0] = translate[row][col];
	      stemp2[1] = '\0';
	      strlcpy (text, stemp2, textsiz);
	    }
	    else {
	      errors++;
	      strlcpy (text, "", textsiz);
	      if (! quiet) {
	        text_color_set (DW_COLOR_ERROR);
	        dw_printf ("Two digits to letter: Invalid combination \"%c%c\".\n", c1, c2);
	      }
	    }
	  }
	  else {
	    errors++;
	    strlcpy (text, "", textsiz);
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Two digits to letter: Second character \"%c\" must be in range of 1 through 4.\n", c2);
	    }
	  }
	}
	else {
	  errors++;
	  strlcpy (text, "", textsiz);
	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Two digits to letter: First character \"%c\" must be in range of 2 through 9.\n", c1);
	  }
	}

	return (errors);     

} /* end tt_two_digits_to_letter */


/*------------------------------------------------------------------
 *
 * Name:        tt_call10_to_text
 *
 * Purpose:     Convert the 10 digit callsign representation to text.
 *
 * Inputs:      buttons	- Input string.
 *			  Should contain only ten digits.
 *
 *		quiet	- True to suppress error messages.
 *	
 * Outputs:	text	- Converted to callsign with upper case letters and digits.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_call10_to_text (const char *buttons, int quiet, char *text)
{
	const char *b;
	char *t;
	char c;
	int packed;		/* from last 4 digits */
	int row, col;
	int errors = 0;
	int k;

	t = text;
	*t = '\0';	/* result */

/* Validity check. */

	if (strlen(buttons) != 10) {

	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Callsign 6+4 to text: Encoded Callsign \"%s\" must be exactly 10 digits.\n", buttons);
	  }
	  errors++;
	  return (errors);
   	}

	for (b = buttons; *b != '\0'; b++) {

	  if (! isdigit(*b)) {
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Callsign 6+4 to text: Encoded Callsign \"%s\" can contain only digits.\n", buttons);
	    }
	    errors++;
	    return (errors);
	  }
   	}

	packed = atoi(buttons+6);

	for (k = 0; k < 6; k++) {
	  c = buttons[k];

	  row = c - '0';
	  col = (packed >> ((5 - k) *2)) & 3;

	  if (row < 0 || row > 9 || col < 0 || col > 3) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Callsign 6+4 to text: INTERNAL ERROR %d %d.  Should not be here.\n", row, col);
	    errors++;
	    row = 0;
	    col = 1;
	  }

	  if (call10encoding[row][col] != 0) {
	    *t++ = call10encoding[row][col];
	    *t = '\0';
	  }
	  else {
	    errors++;
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Callsign 6+4 to text: Invalid combination: button %d, position %d.\n", row, col);
	    }
	  }
	}

/* Trim any trailing spaces. */

	k = strlen(text) - 1;		/* should be 6 - 1 = 5 */

	while (k >= 0 && text[k] == ' ') {
	  text[k] = '\0';
	  k--;
	}

	return (errors);          

} /* end tt_call10_to_text */



/*------------------------------------------------------------------
 *
 * Name:        tt_call5_suffix_to_text
 *
 * Purpose:     Convert the 5 digit APRStt 3 style callsign suffix
 *		representation to text.
 *
 * Inputs:      buttons	- Input string.
 *			  Should contain exactly 5 digits.
 *
 *		quiet	- True to suppress error messages.
 *
 * Outputs:	text	- Converted to 3 upper case letters and/or digits.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_call5_suffix_to_text (const char *buttons, int quiet, char *text)
{
	const char *b;
	char *t;
	char c;
	int packed;		/* from last 4 digits */
	int row, col;
	int errors = 0;
	int k;

	t = text;
	*t = '\0';	/* result */

/* Validity check. */

	if (strlen(buttons) != 5) {

	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Callsign 3+2 suffix to text: Encoded Callsign \"%s\" must be exactly 5 digits.\n", buttons);
	  }
	  errors++;
	  return (errors);
	}

	for (b = buttons; *b != '\0'; b++) {

	  if (! isdigit(*b)) {
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Callsign 3+2 suffix to text: Encoded Callsign \"%s\" can contain only digits.\n", buttons);
	    }
	    errors++;
	    return (errors);
	  }
	}

	packed = atoi(buttons+3);

	for (k = 0; k < 3; k++) {
	  c = buttons[k];

	  row = c - '0';
	  col = (packed >> ((2 - k) * 2)) & 3;

	  if (row < 0 || row > 9 || col < 0 || col > 3) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Callsign 3+2 suffix to text: INTERNAL ERROR %d %d.  Should not be here.\n", row, col);
	    errors++;
	    row = 0;
	    col = 1;
	  }

	  if (call10encoding[row][col] != 0) {
	    *t++ = call10encoding[row][col];
	    *t = '\0';
	  }
	  else {
	    errors++;
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("Callsign 3+2 suffix to text: Invalid combination: button %d, position %d.\n", row, col);
	    }
	  }
	}

	if (errors > 0) {
	  strcpy (text, "");
	  return (errors);
	}

	return (errors);

} /* end tt_call5_suffix_to_text */



/*------------------------------------------------------------------
 *
 * Name:        tt_mhead_to_text	
 *
 * Purpose:     Convert the DTMF representation of 
 *		Maidenhead Grid Square Locator to normal text representation.
 *
 * Inputs:      buttons	- Input string.
 *			  Must contain 4, 6, 10, or 12, 16, or 18 digits.
 *
 *		quiet	- True to suppress error messages.
 *	
 * Outputs:	text	- Converted to gridsquare with upper case letters and digits.
 *			  Length should be 2, 4, 6, or 8 with alternating letter or digit pairs.
 *			  Zero length if any error.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

#define MAXMHPAIRS 6

static const struct {
	char *position;
	char  min_ch;
	char  max_ch;
} mhpair[MAXMHPAIRS] = {
	{ "first",  'A', 'R' },
	{ "second", '0', '9' },
	{ "third",  'A', 'X' },
	{ "fourth", '0', '9' },
	{ "fifth",  'A', 'X' },
	{ "sixth",  '0', '9' }
};


int tt_mhead_to_text (const char *buttons, int quiet, char *text, size_t textsiz)
{
	const char *b;
	int errors = 0;

	strlcpy (text, "", textsiz);

/* Validity check. */

	if (strlen(buttons) != 4 && strlen(buttons) != 6 &&
	    strlen(buttons) != 10 && strlen(buttons) != 12 &&
	    strlen(buttons) != 16 && strlen(buttons) != 18) {

	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" must be exactly 4, 6, 10, or 12 digits.\n", buttons);
	  }
	  errors++;
	  strlcpy (text, "", textsiz);
	  return (errors);
   	}

	for (b = buttons; *b != '\0'; b++) {

	  if (! isdigit(*b)) {
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("DTMF to Maidenhead Gridsquare Locator: Input \"%s\" can contain only digits.\n", buttons);
	    }
	    errors++;
	    strlcpy (text, "", textsiz);
	    return (errors);
	  }
   	}


/* Convert DTMF to normal representation. */

	b = buttons;

	int n;

	for (n = 0; n < 6 && b < buttons+strlen(buttons); n++) {
	  if ((n % 2) == 0) {

	    /* Convert pairs of digits to letter. */

	    char t2[2];

	    errors += tt_two_digits_to_letter (b, quiet, t2, sizeof(t2));
	    strlcat (text, t2, textsiz);
	    b += 2;

	    errors += tt_two_digits_to_letter (b, quiet, t2, sizeof(t2));
	    strlcat (text, t2, textsiz);
	    b += 2;
	  }
	  else {

	    /* Copy the digits. */

	    char d3[3];
	    d3[0] = *b++;
	    d3[1] = *b++;
	    d3[2] = '\0';
	    strlcat (text, d3, textsiz);
	  }
	}

	if (errors != 0) {
	  strlcpy (text, "", textsiz);
	}
	return (errors);          

} /* end tt_mhead_to_text */


/*------------------------------------------------------------------
 *
 * Name:        tt_text_to_mhead	
 *
 * Purpose:     Convert normal text Maidenhead Grid Square Locator to DTMF representation.
 *
 * Inputs:	text	- Maidenhead Grid Square locator in usual format.
 *			  Length should be 1 to 6 pairs with alternating letter or digit pairs.
 *
 *		quiet	- True to suppress error messages.
 *
 *		buttonsize - space available for 'buttons' result.
 *
 * Outputs:	buttons	- Result with 4, 6, 10, 12, 16, 18 digits.
 *			  Each letter is replaced by two digits.
 *			  Digits are simply copied.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_text_to_mhead (const char *text, int quiet, char *buttons, size_t buttonsize)
{
	int errors = 0;
	int np, i;

	strlcpy (buttons, "", buttonsize);

	np = strlen(text) / 2;

	if ((strlen(text) % 2) != 0) {

	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Maidenhead Gridsquare Locator to DTMF: Input \"%s\" must be even number of characters.\n", text);
	  }
	  errors++;
	  return (errors);
   	}

	if (np < 1 || np > MAXMHPAIRS) {

	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("Maidenhead Gridsquare Locator to DTMF: Input \"%s\" must be 1 to %d pairs of characters.\n", text, np);
	  }
	  errors++;
	  return (errors);
   	}

	for (i = 0; i < np; i++) {

	  char t0 = text[i*2];
	  char t1 = text[i*2+1];

	  if (toupper(t0) < mhpair[i].min_ch || toupper(t0) > mhpair[i].max_ch ||
		toupper(t1) < mhpair[i].min_ch || toupper(t1) > mhpair[i].max_ch) {
	    if (! quiet) {
	      text_color_set(DW_COLOR_ERROR);
	      dw_printf("The %s pair of characters in Maidenhead locator \"%s\" must be in range of %c thru %c.\n", 
				mhpair[i].position, text, mhpair[i].min_ch, mhpair[i].max_ch);
	    }
	    strlcpy (buttons, "", buttonsize);
	    errors++;  
	    return(errors);
	  }

	  if (mhpair[i].min_ch == 'A') {		/* Should be letters */

	    char b3[3];

	    errors += tt_letter_to_two_digits (t0, quiet, b3);
	    strlcat (buttons, b3, buttonsize);

	    errors += tt_letter_to_two_digits (t1, quiet, b3);
	    strlcat (buttons, b3, buttonsize);
	  }
	  else {					/* Should be digits */

	    char b3[3];

	    b3[0] = t0;
	    b3[1] = t1;
	    b3[2] = '\0';
	    strlcat (buttons, b3, buttonsize);
	  }
	}

	if (errors != 0) strlcpy (buttons, "", buttonsize);

	return (errors);          

} /* tt_text_to_mhead */


/*------------------------------------------------------------------
 *
 * Name:        tt_satsq_to_text	
 *
 * Purpose:     Convert the 4 digit DTMF special Satellite gridsquare to normal 2 letters and 2 digits.
 *
 * Inputs:      buttons	- Input string.
 *			  Should contain 4 digits.
 *
 *		quiet	- True to suppress error messages.
 *	
 * Outputs:	text	- Converted to gridsquare with upper case letters and digits.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_satsq_to_text (const char *buttons, int quiet, char *text)
{
	const char *b;
	int row, col;
	int errors = 0;

	strcpy (text, "");

/* Validity check. */

	if (strlen(buttons) != 4) {

	  if (! quiet) {
	    text_color_set (DW_COLOR_ERROR);
	    dw_printf ("DTMF to Satellite Gridsquare: Input \"%s\" must be exactly 4 digits.\n", buttons);
	  }
	  errors++;
	  return (errors);
   	}

	for (b = buttons; *b != '\0'; b++) {

	  if (! isdigit(*b)) {
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("DTMF to Satellite Gridsquare: Input \"%s\" can contain only digits.\n", buttons);
	    }
	    errors++;
	    return (errors);
	  }
   	}

	row = buttons[0] - '0';
	col = buttons[1] - '0';

	strcpy (text, grid[row][col]);
	strcat (text, buttons+2);

	return (errors);          

} /* end tt_satsq_to_text */



/*------------------------------------------------------------------
 *
 * Name:        tt_ascii2d_to_text
 *
 * Purpose:     Convert the two digit ascii representation back to normal text.
 *
 * Inputs:      buttons	- Input string.
 *			  Should contain pairs of digits in range 00 to 94.
 *
 *		quiet	- True to suppress error messages.
 *
 * Outputs:	text	- Converted to any printable ascii characters.
 *
 * Returns:     Number of errors detected.
 *
 *----------------------------------------------------------------*/

int tt_ascii2d_to_text (const char *buttons, int quiet, char *text)
{
	const char *b = buttons;
	char *t = text;
	char c1, c2;
	int errors = 0;


	*t = '\0';

	while (*b != '\0') {

	  c1 = *b++;
	  if (*b != '\0') {
	    c2 = *b++;
	  }
	  else {
	    c2 = ' ';
	  }

	  if (isdigit(c1) && isdigit(c2)) {
	    int n;

	    n = (c1 - '0') * 10 + (c2 - '0');

           *t++ = n + 32;
	   *t = '\0';
	  }
	  else {

/* Unexpected character. */

	    errors++;
	    if (! quiet) {
	      text_color_set (DW_COLOR_ERROR);
	      dw_printf ("ASCII2D to text: Invalid character pair \"%c%c\".\n", c1, c2);
	    }
	  }
	}
	return (errors);

} /* end tt_ascii2d_to_text */



/*------------------------------------------------------------------
 *
 * Name:        tt_guess_type
 *
 * Purpose:     Try to guess which encoding we have.
 *
 * Inputs:      buttons	- Input string.
 *			  Should contain only 0123456789ABCD.
 *
 * Returns:     TT_MULTIPRESS	- Looks like multipress.
 *		TT_TWO_KEY	- Looks like two key.
 *		TT_EITHER	- Could be either one.
 *
 *----------------------------------------------------------------*/

typedef enum { TT_EITHER, TT_MULTIPRESS, TT_TWO_KEY } tt_enc_t;

tt_enc_t tt_guess_type (char *buttons) 
{
	char text[256];
	int err_mp;
	int err_tk;
	
/* If it contains B, C, or D, it can't be multipress. */

	if (strchr (buttons, 'B') != NULL || strchr (buttons, 'b') != NULL ||
	    strchr (buttons, 'C') != NULL || strchr (buttons, 'c') != NULL ||
	    strchr (buttons, 'D') != NULL || strchr (buttons, 'd') != NULL) {
	  return (TT_TWO_KEY);
	}

/* Try parsing quietly and see if one gets errors and the other doesn't. */

	err_mp = tt_multipress_to_text (buttons, 1, text);
	err_tk = tt_two_key_to_text (buttons, 1, text);

	if (err_mp == 0 && err_tk > 0) {
	  return (TT_MULTIPRESS);
 	}
	else if (err_tk == 0 && err_mp > 0) {
	  return (TT_TWO_KEY);
	}

/* Could be either one. */

	return (TT_EITHER);

} /* end tt_guess_type */



/*------------------------------------------------------------------
 *
 * Name:        main
 *
 * Purpose:     Utility program for testing the encoding.
 *
 *----------------------------------------------------------------*/


#if ENC_MAIN

int checksum (char *tt)
{
	int cs = 10;	/* Assume leading 'A'. */
			/* Doesn't matter due to mod 10 at the end. */
	char *p;

	for (p = tt; *p != '\0'; p++) {
	  if (isdigit(*p)) {
	    cs += *p - '0';
	  }
	  else if (isupper(*p)) {
	    cs += *p - 'A' + 10;
	  }
	  else if (islower(*p)) {
	    cs += *p - 'a' + 10;
	  }
	}

	return (cs % 10);
}

int main (int argc, char *argv[])
{
	char text[1000], buttons[2000];
	int n;
	int cs;

	text_color_set (DW_COLOR_INFO);

	if (argc < 2) {
	  text_color_set (DW_COLOR_ERROR);
	  dw_printf ("Supply text string on command line.\n");
	  exit (1);
	}

	strcpy (text, argv[1]);

	for (n = 2; n < argc; n++) {
	  strcat (text, " ");
	  strcat (text, argv[n]);
	}

	dw_printf ("Push buttons for multi-press method:\n");
	n = tt_text_to_multipress (text, 0, buttons);
	cs = checksum (buttons);
	dw_printf ("\"%s\"    checksum for call = %d\n", buttons, cs);

	dw_printf ("Push buttons for two-key method:\n");
	n = tt_text_to_two_key (text, 0, buttons);
	cs = checksum (buttons);
	dw_printf ("\"%s\"    checksum for call = %d\n", buttons, cs);

	n = tt_text_to_call10 (text, 1, buttons);
	if (n == 0) {
	  dw_printf ("Push buttons for fixed length 10 digit callsign:\n");
	  dw_printf ("\"%s\"\n", buttons);
	}

	n = tt_text_to_mhead (text, 1, buttons, sizeof(buttons));
	if (n == 0) {
	  dw_printf ("Push buttons for Maidenhead Grid Square Locator:\n");
	  dw_printf ("\"%s\"\n", buttons);
	}

	n = tt_text_to_satsq (text, 1, buttons, sizeof(buttons));
	if (n == 0) {
	  dw_printf ("Push buttons for satellite gridsquare:\n");
	  dw_printf ("\"%s\"\n", buttons);
	}

	return(0);

}  /* end main */

#endif		/* encoding */


/*------------------------------------------------------------------
 *
 * Name:        main
 *
 * Purpose:     Utility program for testing the decoding.
 *
 *----------------------------------------------------------------*/


#if DEC_MAIN


int main (int argc, char *argv[])
{
	char buttons[2000], text[1000];
	int n;

	text_color_set (DW_COLOR_INFO);

	if (argc < 2) {
	  text_color_set (DW_COLOR_ERROR);
	  dw_printf ("Supply button sequence on command line.\n");
	  exit (1);
	}

	strcpy (buttons, argv[1]);

	for (n = 2; n < argc; n++) {
	  strlcat (buttons, argv[n], sizeof(buttons));
	}

	switch (tt_guess_type(buttons)) {
	  case TT_MULTIPRESS:
	    dw_printf ("Looks like multi-press encoding.\n");
	    break;
	  case TT_TWO_KEY:
	    dw_printf ("Looks like two-key encoding.\n");
	    break;
	  default:
	    dw_printf ("Could be either type of encoding.\n");
	    break;
	}

	dw_printf ("Decoded text from multi-press method:\n");
	n = tt_multipress_to_text (buttons, 0, text);
	dw_printf ("\"%s\"\n", text);

	dw_printf ("Decoded text from two-key method:\n");
	n = tt_two_key_to_text (buttons, 0, text);
	dw_printf ("\"%s\"\n", text);

	n = tt_call10_to_text (buttons, 1, text);
	if (n == 0) {
	  dw_printf ("Decoded callsign from 10 digit method:\n");
	  dw_printf ("\"%s\"\n", text);
	}

	n = tt_mhead_to_text (buttons, 1, text, sizeof(text));
	if (n == 0) {
	  dw_printf ("Decoded Maidenhead Locator from DTMF digits:\n");
	  dw_printf ("\"%s\"\n", text);
	}

	n = tt_satsq_to_text (buttons, 1, text);
	if (n == 0) {
	  dw_printf ("Decoded satellite gridsquare from 4 DTMF digits:\n");
	  dw_printf ("\"%s\"\n", text);
	}

	return(0);

}  /* end main */

#endif		/* decoding */


#if TTT_TEST

/* gcc -g -DTTT_TEST tt_text.c textcolor.o misc.a && ./a.exe */


/* Quick unit test. */

static int error_count;

static void test_text2tt (char *text, char *expect_mp, char *expect_2k, char *expect_c10, char *expect_loc, char *expect_sat)
{
	char buttons[100];

	text_color_set(DW_COLOR_INFO);
	dw_printf ("\nConvert from text \"%s\" to tone sequence.\n", text);

	tt_text_to_multipress (text, 0, buttons);
	if (strcmp(buttons, expect_mp) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected multi-press \"%s\" but got \"%s\"\n", expect_mp, buttons); }

	tt_text_to_two_key (text, 0, buttons);
	if (strcmp(buttons, expect_2k) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected two-key \"%s\" but got \"%s\"\n", expect_2k, buttons); }

	tt_text_to_call10 (text, 0, buttons);
	if (strcmp(buttons, expect_c10) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected call 6+4 \"%s\" but got \"%s\"\n", expect_c10, buttons); }

	tt_text_to_mhead (text, 0, buttons, sizeof(buttons));
	if (strcmp(buttons, expect_loc) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected Maidenhead \"%s\" but got \"%s\"\n", expect_loc, buttons); }

	tt_text_to_satsq (text, 0, buttons, sizeof(buttons));
	if (strcmp(buttons, expect_sat) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected Sat Sq \"%s\" but got \"%s\"\n", expect_sat, buttons); }
}

static void test_tt2text (char *buttons, char *expect_mp, char *expect_2k, char *expect_c10, char *expect_loc, char *expect_sat)
{
	char text[100];

	text_color_set(DW_COLOR_INFO);
	dw_printf ("\nConvert tone sequence \"%s\" to text.\n", buttons);

	tt_multipress_to_text (buttons, 0, text);
	if (strcmp(text, expect_mp) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected multi-press \"%s\" but got \"%s\"\n", expect_mp, text); }

	tt_two_key_to_text (buttons, 0, text);
	if (strcmp(text, expect_2k) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected two-key \"%s\" but got \"%s\"\n", expect_2k, text); }

	tt_call10_to_text (buttons, 0, text);
	if (strcmp(text, expect_c10) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected call 6+4 \"%s\" but got \"%s\"\n", expect_c10, text); }

	tt_mhead_to_text (buttons, 0, text, sizeof(text));
	if (strcmp(text, expect_loc) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected Maidenhead \"%s\" but got \"%s\"\n", expect_loc, text); }

	tt_satsq_to_text (buttons, 0, text);
	if (strcmp(text, expect_sat) != 0) { error_count++; text_color_set(DW_COLOR_ERROR); dw_printf ("Expected Sat Sq \"%s\" but got \"%s\"\n", expect_sat, text); }
}


int main (int argc, char *argv[])
{

	text_color_set (DW_COLOR_INFO);
	dw_printf ("Test conversions between normal text and DTMF representation.\n");
	dw_printf ("Some error messages are normal.  Just look for number of errors at end.\n");

	error_count = 0;

		    /* original text   multipress                         two-key                 call10        mhead         satsq */

	test_text2tt ("abcdefg 0123", "2A22A2223A33A33340A00122223333",  "2A2B2C3A3B3C4A0A0123", "",           "",            "");

	test_text2tt ("WB4APR",       "922444427A777",                   "9A2B42A7A7C",          "9242771558", "",            "");

	test_text2tt ("EM29QE78",     "3362222999997733777778888",       "3B6A297B3B78",          "",          "326129723278", "");

	test_text2tt ("FM19",         "3336199999",                      "3C6A19",                "3619003333", "336119",       "1819");


		    /* tone_seq                          multipress       two-key                     call10        mhead         satsq */

	test_tt2text ("2A22A2223A33A33340A00122223333",  "ABCDEFG 0123", "A2A222D3D3334 00122223333", "",           "",            "");

	test_tt2text ("9242771558",                      "WAGAQ1KT",     "9242771558",                "WB4APR",     "",            "");

	test_tt2text ("326129723278",                    "DAM1AWPADAPT", "326129723278",               "",          "EM29QE78",    "");

	test_tt2text ("1819",                            "1T1W",         "1819",                       "",           "",           "FM19");


	if (error_count > 0) {

	  text_color_set (DW_COLOR_ERROR);
	  dw_printf ("\nERROR: %d tests failed.\n", error_count);
	  exit (EXIT_FAILURE);
	}

	text_color_set (DW_COLOR_REC);
	dw_printf ("\nSUCCESS!  All tests passed.\n");
	exit (EXIT_SUCCESS);


}  /* end main */

#endif

/* end tt_text.c */