// BUG I think .vcf wrongly contains double colon eg:EMAIL;WORK::a@b.c
#ifdef	ournix
	/* printf("XX %d\n",__LINE__); */
#include "ournix.h"
#endif
	char sccsID[] =
 "@(#) phone.c V1.17 Copyright Julian H. Stacey, Munich, 1997-01-02 - 2018-02-23.\n";

/* FUNCTION : Filter phone book input to output for estic, isdnd,
 *		gnokii, or VCF vcard phone table formats
 *		LATER allow multiple work phone numbers per person.
 *		LATER maybe extend to sort phone book entries by criteria,
 *		such as alphabetic by first name.
 * There's no call to free() 'cos I had once planned to later add a sort command.
 *
 * JJLATER I could add code to allow lots of cascade numbers for a few people 
 * who phone me from lots of different extensions at work, that would shrink total of VCF records.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>	/* For isalpha */

#define strequ(a,b)	(!strcmp(a,b))
#define strnequ(a,b,n)	(!strncmp(a,b,n))

typedef char	FLAG ;

char	**ARGV ;
FLAG	opt_isdn_and_estic =	(FLAG)0 ;
		/* Produce lookup table for isdn & estic */
FLAG	opt_mobile =		(FLAG)0 ;
		/* Produce lookup table for gnokii, for dumb mobile phones */
FLAG	opt_estic =		(FLAG)0 ;
		/* Produce lookup table for estic PBC/LCO/Tk-Anlage */
FLAG	opt_vcard =		(FLAG)0 ;
		/* Produce VCF vcard for Android smart phones */
FLAG	opt_comma =		(FLAG)0 ;
		/* If set, & if opt_mobile also set,
		   convert comma in names to ampersand */
FLAG	opt_new_gnokii =
#if ( __FreeBSD__ == 4 )	/* {{ Assume FreeBSD-4.11 with Gnokii-6.4  */
				(FLAG)0 ;
#else				/* }{ Assume FreeBSD-6.2 with Gnokii-6.14 */
				(FLAG)1 ;
#endif				/*}}*/

FLAG	opt_dflt_exclude =	(FLAG)0 ;
		/* If set, exclude all entries lacking "ie:+" */
FLAG	opt_sort =		(FLAG)0 ; /* Dummy, code incomplete */
FLAG	opt_zap =		(FLAG)0 ; /* Remove unlikely short numbers */
FLAG	opt_capital =		(FLAG)0 ;
		/* Capitalise First Character of space seperated names */
FILE	*fp_in ;
long	line_count ;
char	*file_name ;

/*	brackets.c: [ */
char	seperator[] =	"][" ;
/*	brackets.c: ] */

size_t	seperator_ln ;

	/* My default full international number is (last bit faked) "+49.89.123456" */
#define MY_COUNTRY	"49"	/* Default international number : Germany */
#define MY_TOWN		"89"	/* Default City or Town : Munich */
#define PREFIX_INTERNATIONAL "00" /* Replace + with 00 for countries */
#define PREFIX_NATIONAL	"0"	/* Dial 0 to get out of town */
char my_country[20]		; /* Typically 49 */
char my_town[20]		; /* Typically 89 */
char prefix_international[20]	; /* Typically 00 to get out of country */
char prefix_national[20]	; /* Typically 0 to select another town in same country */

FLAG num_local = (FLAG)1 ;	/* Output local within-same-town variant of
				   number, if within your town */
FLAG num_national = (FLAG)1 ;	/* Output within-same-nation national variant
				   of number, if within your country */
FLAG num_international = (FLAG)1 ;	/* Output full international number */
FLAG num_one = (FLAG)0 ;	/* Just one number, the shortest */
FLAG field_printed = (FLAG)0 ;


char int_prefix_country[20] ;	/* Typically 0049 */
char nat_prefix_town[20] ;	/* Typically 089 */
unsigned ln_nat_prefix_town,	ln_int_prefix_country ;
unsigned ln_prefix_national,	ln_prefix_international ;

#define SIM_MAX	100		/* Maximum number of names in a SIM card */
unsigned sim_max = SIM_MAX ;

/* Define Maximum names in phone internal memory. */
#if defined NOKIA_6110	/*{{*/
#define PHONE_MAX 50		/* Erik S.: GSM spec requires > 50. */
#elif defined NOKIA_5110	/*}{*/
#define PHONE_MAX 0		/* Xgnokii */
#elif defined NOKIA_6210	/*}{*/
#define PHONE_MAX 500		/* Pawel Kot <pkot_ERASE@linuxnews.pl> */
#elif defined SONY_CMD_J70 /*}{*/
#define PHONE_MAX 500
#elif defined SONY_PTX_520 /*}{*/
#define PHONE_MAX 500
#else
#define PHONE_MAX 50		/* Be conservative, (see eg Nokia above) */
#endif	/*}}*/

/* Define maximum length of name to be stored for mobile phones */
#define	NAME_WIDTH_MAX	64
#define	NAME_WIDTH_MIN	3	/* Nothing special about 3, but program not
				 * written for daft values example <=2,
				 * & probably would crash.
				 */
#ifdef NOKIA_6110	/*{{*/
#define	NAME_WIDTH	14
#else	/*}{ (Sony 13) */
#define	NAME_WIDTH	14
#endif	/*}}*/
/* My new nokia, when set to large font mode shows just part of name, eg
 *	Original
 *				"Abcde + Abcd12"
 *	Should display as
 *				"Abcde + Abcd12 #"
 *	But gets displayed as
 *				"Abcde + Abcd..."
 */
#undef	NAME_WIDTH
#define	NAME_WIDTH	12
unsigned name_width = NAME_WIDTH ;

unsigned	phone_max = PHONE_MAX ;

#define LINE_LN 1024
char	line_buf[LINE_LN] ;

#define HOME_INDICATOR		"#"	/* Hash, as in 4 walls,
					 * Better than 'H' to distinguish
					 * from 'W' & 'M' in poor light.
					 */
#ifdef OLD	/*{ gnokii running non FreeBSD-4.7 used to be OK with these
		 * but on 4.9 Dollar & and Yen (but not hash) are getting lost, not
		 * by this program, but by gnokii wrinting to 6110,
		 * they show OK in the gnokii window, but after writing to
		 * Nokia 6110 they are no longer visible
		 * until/if I fix that, I use boring letters.
		 */

#define WORK_INDICATOR		"$"	/* Dollar, as in working for money */
#define MOBILE_INDICATOR	"\xa5"	/* Yen symbol, looks like Aerial */
#else	/*}{ So now we use boring letters */
#define WORK_INDICATOR		"W"
#define MOBILE_INDICATOR	"Y"	/* Aerial shape, better than M for
					 * Mobile, as horizontal of M
					 * blurs easier into previous
					 * letter.  + not all call
					 * it mobile, eg Americans
					 * "Cell" & Germans "Handy".
					 */
#endif	/*}*/

#include "person.h"

int person_ln ;

struct person *first_person, *cur_person ;

#define VCARD_LN 1000
char pers_fn[VCARD_LN];
char pers_sn[VCARD_LN];
char pers_bn[VCARD_LN];
FLAG pers_merged ;

unsigned contacts_max = 0 ;	// contacts per vcf book max
unsigned contacts_count = 0 ;	// contacts per vcf book count

	void
init_person(tmp_person)
	struct person *tmp_person ;
	{
	tmp_person->psn_next = (struct person *)0 ;

	  tmp_person->psn_fn = tmp_person->psn_sn = tmp_person->psn_bn
	= tmp_person->psn_de = tmp_person->psn_co = tmp_person->psn_ah
	= tmp_person->psn_aw = tmp_person->psn_th = tmp_person->psn_tw
	= tmp_person->psn_tm = tmp_person->psn_tn = tmp_person->psn_fh
	= tmp_person->psn_fw = tmp_person->psn_fm = tmp_person->psn_mh
	= tmp_person->psn_mw = tmp_person->psn_mm = tmp_person->psn_ih
	= tmp_person->psn_iw = tmp_person->psn_im = tmp_person->psn_ie
	= tmp_person->psn_cg = tmp_person->psn_tx = tmp_person->psn_eh
	= tmp_person->psn_ew = tmp_person->psn_em = tmp_person->psn_wh
	= tmp_person->psn_ww = tmp_person->psn_sk = tmp_person->psn_cy
	= tmp_person->psn_ws = tmp_person->psn_sm = tmp_person->psn_ca
	= tmp_person->psn_oc = tmp_person->psn_na = tmp_person->psn_bo
	= tmp_person->psn_te = tmp_person->psn_fr = tmp_person->psn_ac
	= tmp_person->psn_xs = tmp_person->psn_xr = tmp_person->psn_ld
	= tmp_person->psn_bd = tmp_person->psn_bg = tmp_person->psn_jo
	= tmp_person->psn_cz = tmp_person->psn_zz = (char *)0 ;

	}

	void
print_keys()
	{
	char *p =
 "fn:sn:bn:co:ah:aw:th:tw:tm:tn:fh:fw:fm:mh:mw:mm:ih:iw:im:ie:cg:tx:eh:ew:wp:wb:sk:cy:ws:sm:ca:oc:na:bo:te:fr:ac:xs:xr:ld:bd:bg:jo:cz:zz";
	while (*p != '\0' )
		{
		if (*p == ':' ) (void)fputc((int)',', stderr) ;
		else (void)fputc((int)*p, stderr);
		p++;
		}
	(void)fputc((int)'\n', stderr);
	}

/* Returns pointer to address of required field. */
	/* If enum is supported by MSC V4, could shrink field_p etc */
	char **
field_p(field,tmp_person)
	char		*field ;
	struct person	*tmp_person;
	{
	if (strequ("ac",field)) return( &(tmp_person->psn_ac) );
	if (strequ("ah",field)) return( &(tmp_person->psn_ah) );
	if (strequ("aw",field)) return( &(tmp_person->psn_aw) );
	if (strequ("bd",field)) return( &(tmp_person->psn_bd) );
	if (strequ("bg",field)) return( &(tmp_person->psn_bg) );
	if (strequ("bn",field)) return( &(tmp_person->psn_bn) );
	if (strequ("bo",field)) return( &(tmp_person->psn_bo) );
	if (strequ("ca",field)) return( &(tmp_person->psn_ca) );
	if (strequ("cg",field)) return( &(tmp_person->psn_cg) );
	if (strequ("co",field)) return( &(tmp_person->psn_co) );
	if (strequ("cy",field)) return( &(tmp_person->psn_cy) );
	if (strequ("cz",field)) return( &(tmp_person->psn_cz) );
	if (strequ("de",field)) return( &(tmp_person->psn_de) );
	if (strequ("eh",field)) return( &(tmp_person->psn_eh) );
	if (strequ("em",field)) return( &(tmp_person->psn_em) );
	if (strequ("ew",field)) return( &(tmp_person->psn_ew) );
	if (strequ("fh",field)) return( &(tmp_person->psn_fh) );
	if (strequ("fm",field)) return( &(tmp_person->psn_fm) );
	if (strequ("fn",field)) return( &(tmp_person->psn_fn) );
	if (strequ("fr",field)) return( &(tmp_person->psn_fr) );
	if (strequ("fw",field)) return( &(tmp_person->psn_fw) );
	if (strequ("ie",field)) return( &(tmp_person->psn_ie) );
	if (strequ("ih",field)) return( &(tmp_person->psn_ih) );
	if (strequ("im",field)) return( &(tmp_person->psn_im) );
	if (strequ("iw",field)) return( &(tmp_person->psn_iw) );
	if (strequ("jo",field)) return( &(tmp_person->psn_jo) );
	if (strequ("ld",field)) return( &(tmp_person->psn_ld) );
	if (strequ("mh",field)) return( &(tmp_person->psn_mh) );
	if (strequ("mm",field)) return( &(tmp_person->psn_mm) );
	if (strequ("mw",field)) return( &(tmp_person->psn_mw) );
	if (strequ("na",field)) return( &(tmp_person->psn_na) );
	if (strequ("oc",field)) return( &(tmp_person->psn_oc) );
	if (strequ("sk",field)) return( &(tmp_person->psn_sk) );
	if (strequ("sm",field)) return( &(tmp_person->psn_sm) );
	if (strequ("sn",field)) return( &(tmp_person->psn_sn) );
	if (strequ("te",field)) return( &(tmp_person->psn_te) );
	if (strequ("th",field)) return( &(tmp_person->psn_th) );
	if (strequ("tm",field)) return( &(tmp_person->psn_tm) );
	if (strequ("tn",field)) return( &(tmp_person->psn_tn) );
	if (strequ("tw",field)) return( &(tmp_person->psn_tw) );
	if (strequ("tx",field)) return( &(tmp_person->psn_tx) );
	if (strequ("wh",field)) return( &(tmp_person->psn_wh) );
	if (strequ("ws",field)) return( &(tmp_person->psn_ws) );
	if (strequ("ww",field)) return( &(tmp_person->psn_ww) );
	if (strequ("xr",field)) return( &(tmp_person->psn_xr) );
	if (strequ("xs",field)) return( &(tmp_person->psn_xs) );
	if (strequ("zz",field)) return( &(tmp_person->psn_zz) );

	fprintf(stderr,
		"%s Warning: File %s, Line %ld, Field \"%s\" %s.\n",
		*ARGV, file_name, line_count, field,
		"unrecognised, treating as comment" ) ;
	return( &(tmp_person->psn_cz) );
	}

	int
get_line()	/* Return 0 if fail, as on EOF */
	{
	register int	this_ch, count = LINE_LN ;
	char	*line_ptr = line_buf, ch ;

	line_buf[0] = '\0' ;
	line_count++ ;
	while((this_ch = getc(fp_in)) != EOF)
		{
		ch = (char)this_ch ;
		if (ch == '\n')
			{
			*line_ptr = '\0' ;
			return( line_ptr - line_buf) ;
			}
		*line_ptr++ = ch ;
		if (--count <= 0)
			{
			*--line_ptr = '\0' ; /* The -- loses the ch,
						but need it to avoid writing
						beyond array. */
			fprintf(stderr,
	"%s %s: File %s, Line %ld >= %d chars, too long, break inserted\n",
				*ARGV,
#define SAFER 1	/* Undef for old behaviour */
#ifdef SAFER
				"Error",
#else
				"Warning",
#endif
				file_name, line_count, LINE_LN) ;
#ifdef SAFER
			exit(-1);
#else
			return(LINE_LN-1) ;
#endif
			}
		} /* Loop for next char */
	if (line_ptr>line_buf)
		/* Flush any last line lacking a \n */
		return(line_ptr - line_buf) ;
	else return(EOF);
	}

/* Append to an entry in a person structure */
	void
ent_append( to , from)
	char **to, *from ;
	{
	char *new ; int len ;
	if ((new = realloc( *to,
		(len=strlen(*to)) + 1 + 1 + strlen(from) + 1 ) )
			/* 1st str + \n + space + 2nd str + \0 */
		==(void *)0)
		{perror("ent_append"); exit(EXIT_FAILURE); }
	if ((*from == ' ') || (*from == '\t')) sprintf(new+len,"\n%s", from);
	else
		/* Prepend a space on continuation line */
		sprintf(new+len,"\n %s", from);
	*to = new ;
		/* I guess *to = new is normally un-necessary,
		 * but reading FreeBSD man malloc re. env. var.
		 * MALLOC_OPTIONS:
		 * R ``realloc'' always reallocate when realloc()
		 * is called, even if the initial allocation
		 * was big enough.
		 * This can substantially aid in compacting memory.
		 */
	}

/* Gets an entry such as "sn:stacey" (which may have trailing data on
 * subsequent indented lines).
 *	Return EOF on EOF if no preceeding data.
 *	Return 0 if a person seperator is encountered.
 *	Return 1 if a record is obtained (& don''t exit till the whole
 *		of the multi line record is obtained.)
 */
	int
get_entry(tmp_person)
	struct person *tmp_person;
	{
	char field[3];
	char *data, **ptr, *start;
	int	len ;

	/* On new files input buffer will be empty, but in normal use
	   the read-ahead (necessary to determine if the previous record
	   is terminated) will have already pre-loaded the buffer */
	if (line_buf[0]=='\0')
		{ /* Load input buffer */
		while ((len=get_line()) == 0 ) ; /* Discard blank lines */
		if (len == EOF) return(EOF);
		}
	/* Detect inter person seperator */
	if (strnequ(seperator,line_buf,seperator_ln))
		{ line_buf[0] = '\0' ; return(0) ; }
	if (isalpha(line_buf[0]) && isalpha(line_buf[1]) &&
		(line_buf[2]==':'))
		{ /* New field specifier */
		field[0]=line_buf[0]; field[1]=line_buf[1];field[2]='\0';
		start=line_buf + 3;
		}
	else	{ /* Unspecified data */
		strcpy(field,"cz");
		start=line_buf ;
		fprintf(stderr,
			"%s Warning: File %s, Line %ld, Data \"%s\"\n",
			*ARGV, file_name, line_count, line_buf) ;
		}
	ptr = field_p(field,tmp_person) ;
	if (*ptr == (char *)0)
		{
		if ((data = malloc(strlen(start)+1))==(void *)0)
			{perror("get_entry 1"); exit(EXIT_FAILURE); }
		*ptr = data ;
		strcpy(data,start);

		// (Below added 2018-02-23)
		// Now force all names to begin with upper case
		// Probably it wont suit some culture where names
		// occasionaly begin with lower case, eg maybe de'Brogli or d'eath
		// or some such; but it suits me.
		//
		if ( (	strequ(field, "ac") ||
			strequ(field, "ah") ||
			strequ(field, "aw") ||
			strequ(field, "bd") ||
			strequ(field, "bg") ||
			strequ(field, "bn") ||
			strequ(field, "bo") ||
			strequ(field, "ca") ||
			strequ(field, "cg") ||
			strequ(field, "co") ||
			strequ(field, "cy") ||
			strequ(field, "de") ||
			strequ(field, "fn") ||
			strequ(field, "fr") ||
			strequ(field, "jo") ||
			strequ(field, "ld") ||
			strequ(field, "na") ||
			strequ(field, "oc") ||
			strequ(field, "sk") ||
			strequ(field, "sm") ||
			strequ(field, "sn") ||
			strequ(field, "te") ||
			strequ(field, "ti") ||
			strequ(field, "ws") ||
			strequ(field, "xr") ||
			strequ(field, "xs")
			// Not corrupting these to upper case:
			// "cz" "zz" "eh" "em" "ew" "tx" "wh" "ww"
			) && islower(*data) ) *data = toupper(*data) ;
		}
	else	{ /* Entry used previously, so append */
		fprintf(stderr,
	"Warning file: %s, line: %ld, field: %s, appending \"%s\" to \"%s\"\n",
			file_name, line_count, field,  start, *ptr);
		ent_append( ptr , start);
		}
	/* Append cascade indented entry extensions */
	while (((len=get_line()) != 0 ) && ( len != EOF ) &&
		((line_buf[0] ==' ') || (line_buf[0] =='\t')))
		/* If we happen to get an EOF now, leave it till
			next get_entry() invocation. */
		ent_append( ptr , line_buf);
	return(1);
	}

	int
get_person()
	{
	struct person *new_person ;
	int	rslt ;
	if ((new_person = malloc(sizeof(struct person)))==(void *)0)
			{perror("get_person"); exit(EXIT_FAILURE); }
	init_person(new_person);
	if (first_person == (struct person *)0)
		cur_person= first_person = new_person ;
	else	{
		cur_person->psn_next = new_person ;
		cur_person = new_person ;
		}
	while((rslt=get_entry(cur_person)) && (rslt != EOF)) ;
	return(rslt);
	}


/* Receive an empty string & fill it with an incrementing record number.
 * Used by SIM cards & gnokii for dumb mobile phones.
 */
	char *
get_index(phone_index)
	char *phone_index;
	{
	static int sim_count=1 ;
	/* Xgnokii starts numbering both at 1 not 0 */
	static int phone_count=1 ;

	if (phone_count == phone_max + 1 )
		fprintf(stderr,
		"Warning: You exceed assumed maximum memory of phone\n");
		/* Do not abort, that''s the phones job later */
	sprintf(phone_index,"%s;%d;",
		( sim_count <= sim_max ) ?
			((opt_new_gnokii == (FLAG)0 ) ? "A" : "SM" )
			:
			((opt_new_gnokii == (FLAG)0 ) ? "B" : "ME")
			,
		( sim_count <= sim_max ) ?	sim_count++ :  phone_count++
		) ;
	return(phone_index);
	}

/* Dumb mobile phones support about 5 or 6 call groups.
 * I think smart mobiles may support more call groups.
 * Generally I would be thinking to use different ring tones
 * for different call groups
 */
	int
get_call_group(call_group,name)
	char *call_group;
	char *name;
	{
	char *p;
	if ((p=call_group)==(char *)0) return(5) ;
	if (*p=='\0') return(5) ;
	while (*p != '\0') { *p = tolower(*p); p++ ; }
	p = call_group ;
	if (strequ("family",p)) return(0);
	if (strequ("urgent",p)) return(1);
	if (strequ("social",p)) return(2);
	if (strequ("business",p)) return(3);
	if (strequ("club",p)) return(4);
	if (strequ("default",p)) return(5);
	if (strequ("no group",p)) return(5);
	fprintf(stderr,"Unrecognised call group \"%s\" for name \"%s\"\n",
		p,name);
	return(5);
	}

	void
get_file()
	{
	line_count = 0L ;
	line_buf[0] = '\0' ;
	cur_person = first_person = (struct person *)0 ;
	while( get_person() != EOF ) ;
	}

	int
isphone(char *str)	/* Check for valid phone numbers */
	/* JJLATER maybe allow for '*' '#' 'R' as dial keys.
	 * Android Phone app allows to enter: TEL;CELL;PREF:+123#()*-./Npw
	 * Android also allows space characters.
	 * I long ago replaced spaces in my phone.c book with dots,
	 * which idea I believe I got from Hayes
	 * http://en.wikipedia.org/wiki/Hayes_command_set
	 */
	{
	char *ptr ;
	for (ptr = str ;
		(isdigit((int)*ptr) || (*ptr == '.') || (*ptr == '+') ) ;
		ptr++ ) ;
	if (*ptr == '\0' ) return 1 ;
	fprintf(stderr, "Error, bad phone number: \"%s\"\n", str);
	return 0 ;
	}

#define	NAME_LN	100 /* Maximum output for fn: sn: or bn: record */
		/* In fact all of estic gnokii & isdn want much shorter names */
#define NAME_BIG (3 * NAME_LN + 3)	/* 3 is 2 seperators + null */

/* Print a pair of numbers & names as part of a lookup list */
	int
put_pair(phone, name, location, call_group)
	char *phone, *name, *location, *call_group ;
	{
	char name_int[NAME_BIG];
		/* Internal copy as external name
		 * is passed to us several
		 * times & we dont want to
		 * succesively append
		 * all of
		 *	MOBILE_INDICATOR
		 *	WORK_INDICATOR
		 *	HOME_INDICATOR
		 */
	char phone_index[20] ;
#define BUFF	200		/* LATER formalise */
	char buf1[BUFF], buf2[BUFF] ;
	char *p1, *p2 ;
	FLAG	num_done = (FLAG)0 ;

	/* JJLATER Checking should be moved from output to input !  */
	/* Skip incomplete records, comments beginning with a space etc */
	if (	(phone == (char *)0)	|| (*phone == '\0' )	||
		(name == (char *)0)	|| (*name == '\0' )	)
		return -1 ;
	if (*phone == '#') return -1 ;
		/* Hashed out entry, ignore. JJLATER unfortunately my generic
		 * comment delimeter of '#' for this program is also a valid 
		 * dial char. on modern phones, so currently there is no way 
		 * to store such a dial char.
		 */
	if (isspace((int)*phone))
		{
		/* JJLATER add code to be lenient & eat the space */
		fprintf(stderr, "Warning: bad entry: \"%s:%s\" \"%s\"\n",
			(location == (char *)0) ? "" : location ,
			(phone == (char *)0) ? "" : phone ,
			(name == (char *)0) ? "" : name ) ;
		return -1 ;
		}
	if ((p1=strchr(phone,(int)'\n')) != (char *)0)
		/* Unexpected \n, but accept it */ *p1 = '\0' ;
	p1 = phone ;
	if ( *p1 == '+') p1++ ;
	while ( (isdigit ((int)*p1)) || ( *p1 == '.') ) p1++ ;
	if ( *p1 == '\0' ) goto stripped;	/* Ideal, no trailing junk */
	/* Trailing junk exists beyond number */
	p2 = p1 ;
	if ( *p2 == '#' ) { *p2 = '\0' ; goto stripped; }
	/* Strip white before comment delimiter */
	while (isblank ((int)*p2)) p2++ ;
	if ( *p2 == '\0' ) goto stripped;
	if ( *p2 == '#' ) { *p1 = '\0' ; goto stripped; }
	fprintf(stderr,
		"Warning: bad entry: \"%s:%s\" \"%s\"\n",
		(location == (char *)0) ? "" : location ,
		(phone == (char *)0) ? "" : phone ,
		(name == (char *)0) ? "" : name ) ;
	if (isdigit((int)(*p2)))
		/* Bad format "+49.1234 567890" */
		fprintf(stderr, "Warning: discarded numeric %s\n", p2);
	*p1 = '\0' ;
stripped:

	if (*phone == '\0') return -1 ;

	if (!isphone(phone)) return -1 ;

	strcpy(name_int,name);

	/* Capitalise at least First Character of each space seperated name */
	if (islower(name_int[0])) name_int[0] = toupper(name_int[0]) ;
	for(p1 = name_int; *p1 != '\0' ; p1++ )
		{
		if (opt_capital == (FLAG)0)
			{
			if ((*p1 == ' ') && islower(*(p1+1)))
				*(p1+1) = toupper(*(p1+1));
			}
		else	/* Capitalise all */ *(p1+1) = toupper(*(p1+1));
		}

	/* Remove all '.' & ',' & '-' & 'x' (x for extension maybe) */
	for(p1 = phone, p2 = buf2 ; *p1 != '\0' ; p1++ )
		if ((*p1 != '.') && (*p1 != ',') &&
			( *p1 != '-') && (*p1 != 'x') )
			*p2++ = *p1 ;
	*p2 = '\0' ;

	if (opt_zap && (strlen(buf2) < 6 ))
		{ /* Remove unlikely short numbers */
		return -1 ;
		}
	else if (*buf2 == '\0') return -1 ;

	/* Reduce the number that may be in a local or full international
	   form, to it''s minimal form, IE if we are in the same
	   country or town, discard any redundant prefixes */

	if (buf2[0] == '+')
		{ /* Convert "+" to "00" */
		/* International */
		strcpy(buf1,prefix_international);
		strcat(buf1,buf2 + 1);
		}
	else strcpy(buf1,buf2);
	if (strnequ(buf1,int_prefix_country,ln_int_prefix_country))
		{ /* Convert "0049" to "0" */
		strcpy(buf2,prefix_national);
		strcat(buf2,buf1 + ln_int_prefix_country);
		}
	else strcpy(buf2,buf1);
	if (strnequ(buf2,nat_prefix_town,ln_nat_prefix_town))
		{ /* Convert "089" to "" */
		strcpy(buf1,buf2 + ln_nat_prefix_town); }
	else strcpy(buf1,buf2) ;

	/* buf1 now contains shortest version of number, having removed
	   any town & national prefixes that we are situated in */

	if (strlen(buf1) < 3)
		/* 112		Emergency operator (Fire Police Ambulance) */
		/* 140		Alpin Notruf */
		/* 6360		Siemens Perlach */
		/* 19202	City Ambulanz */
		fprintf(stderr,
		"Warning: suspect short number: \"%s\" \"%s\" \"%s\"\n",
		buf1 , name_int , location ) ;

	if (opt_mobile)
		{
		/* Append a location symbol to names:
		 *	$	: Work		: Hint Dollar Money
		 *	#	: Home		: Hint ``Own 4 walls''
		 *	Yen	: Mobile	: Hint: Looks like Antenna
		 */

		if (strequ(location,"TH") || strequ(location,"TW") ||
			strequ(location,"TM") || strequ(location,"TN") )
			name_int[name_width -1] ='\0' ;
		if (strequ(location,"TH")|| strequ(location,"TW")||
			strequ(location,"TM")
			/* But not || strequ(location,"TN")
			 * which is a phone number of unknown type,
			 * might be coming from eg phone_merge
			 */
			)
			{
					/* Force at least 1 space
					for HOME_INDICATOR, WORK_INDICATOR,
					or MOBILE_INDICATOR */
			if (strlen(name_int) < name_width - 1)
						 strcat(name_int," ") ;
			if strequ(location,"TH") strcat(name_int,
				HOME_INDICATOR ) ;
			else if strequ(location,"TW") strcat(name_int,
				WORK_INDICATOR ) ;
			else if strequ(location,"TM") strcat(name_int,
				MOBILE_INDICATOR ) ;
			}
		}
	/*
	 * Some public phone exchanges
	 * (local [switching] offices in American parlance)
	 * allow one to dial either the full number including all the
	 * international &/or town codes, or the local short form if
	 * you are in that town.
	 * (My Munich exchange will allow me to dial myself within the
	 * 89 city using 089123456, but will not allow me to dial my
	 * international self within the 49 nation using 004989 ).
	 * As people now rove the world with laptops, making calls via
	 * modem & mobile phone, & in case multiple phone logs are merged,
	 * we allow more than one number in isdn & istec lists so,
	 *  Input:
	 *		th:+49.89.123456
	 *	or
	 *		th:123456
	 *	etc
	 *  Output:
	 *	123456		Julian
	 *	089123456       Julian
	 *	004989123456    Julian
	 * But as Nokia mobile in Germany via Deutsch Telekom accepts
	 * full +4989123456 syntax, that''s what''s best to use, & no point
	 * storing duplicates.
	 * Isdnd reports if I call myself: "Call from 89123456 to 123456"
	 * Problem with emergency numbers such as 112 & 0800 !
	 */

	/* Names should not contain a comma, as xgnokii then fails to
         * to do a text to number conversion when parsing a comma
         * seperated list of text names of multiple people to SMS
         * with a single message.
         * But some people may Like commas in names, so just warn by default.
         */

	if (opt_mobile)
		{
		char *s_comma ;
		for (s_comma = name_int ;
			(s_comma=strchr(s_comma, (int)',' )) != (char *)0 ;
			)
			{
			fprintf(stderr,
		"%s Warning: %s %s Comma to Ampersand: %s\n",
				(ARGV == (char **)0) ? "\0" : *ARGV,
				(file_name == (char *)0 ) ? "\0" : file_name ,
				(opt_comma) ? "Converting" :
					"Consider using '-,' option to convert",
				name_int);
		/* JJLATER reporting line number would be useful,
			so this code should be moved to input test */
			if (opt_comma) *s_comma++ = '&' ;
			else break ;
			}
		}

	/* Print local town format if wanted & in this town */
	if (num_local &&
		! (strnequ(buf1,prefix_international,ln_prefix_international) ||
		   strnequ(buf1,prefix_national,ln_prefix_national)))
		{
		if (opt_isdn_and_estic || opt_vcard)
			{
			if (!opt_estic)
				printf("%-30s\t\"%s %s\"\n",buf1,
					location, name_int);
				/* Full name may run off screen,
					 so put location first */
			else	/* Estic allows 16 chars in a name,
					EG: "John Smith H" */
				printf(	"%-30s\t\"%.13s %s\"\n",buf1,
					name_int,location);
				/* Put person''s (short) name before location */
			num_done = (FLAG)1 ;
			}
		else if (opt_mobile)
			{
		/* Mobiles mostly need national or international numbers,
		 * not local, as they dont make any assumption which town
		 * they are in for normal numbers, unlike terrestial phones;
		 * however some few local numbers such as "112"=Emergency
		 * need to be left unchanged, so we cant just turn off
		 * all local numbers with a num_local = (FLAG)0 ;
		 */
			if (
	strequ(buf1,/* Debitel: Auskunft */		"11880") ||
	strequ(buf1,/* Debitel: Hotline */		"22210") ||
	strequ(buf1,/* Debitel: Minutes et More */	"2555") ||
	strequ(buf1,/* German:  Notruf */		"112" ) ||
	strequ(buf1,/* German:  Notruf Alpin */		"140" ) ||
	strequ(buf1,/* German:  Polizei */		"110" ) ||
	strequ(buf1,/* T-Mobil: Auskunft/Vermittlung */	"2555") ||
	strequ(buf1,/* T-Mobil: Konto Audio */		"2000") ||
	strequ(buf1,/* T-Mobil: MobilboxAbfrage */	"3311") ||
	strequ(buf1,/* T-Mobil: PannenService */	"2424") ||
	strequ(buf1,/* T-Mobil: SMS-Operator */		"2522") ||
	strequ(buf1,/* T-Mobil: StauInfo Tegaron */	"2211") ||
	strequ(buf1,/* T-Mobil: T-D1 Service Manager */	"2020") ||
	strequ(buf1,/* T-Mobil: TravelService */	"2525") ||
	strequ(buf1,/* T-Mobil: Verkehrs Info */	"2526") ||
	strequ(buf1,/* T-Mobil: Vermittlung */		"2555") ||
	strequ(buf1,/* T-Mobil: Xtra Service 60 */	"2202") ||

		0 /* Blank line above allows a sort with editor */
			)
			/* A nasty ad hoc table till do it a better way.
			 * Perhaps later by defining anything begining with a
			 * "-" EG "-112" does not get expanded to +4989112
			 */
				{
				printf("%s;%s;%s%d%s\n",
					name_int, buf1,
					get_index(phone_index),
					get_call_group(call_group,name_int) ,
					(opt_new_gnokii==(FLAG)0 ) ? ";" : ""
					) ;
				num_done = (FLAG)1 ;
				}
			}
		}
	/* Print national format if wanted & in this nation */
	if (num_national && !(num_done && num_one) &&
		! strnequ(buf1,prefix_international,ln_prefix_international) )
		{
		buf2[0] = '\0' ;
		if (!strnequ(buf1,prefix_national,ln_prefix_national))
			/* If buf1 is local number */
			strcpy(buf2,nat_prefix_town);
		strcat(buf2,buf1);
		if (opt_isdn_and_estic || opt_vcard)
			{	/* Not single gnokii, so print now,
				 as may want both */
			if (!opt_estic)
				/* Full name may run off screen,
					 so put location first */
				printf("%-30s\t\"%s %s\"\n",buf2,
					location, name_int);
			else
				/* Estic allows 16 chars in a name,
				   put person''s (short) name before
				   location */
				printf("%-30s\t\"%.13s %s\"\n",buf2,
					name_int,location);
			num_done = (FLAG)1 ;
			}
		else if (opt_mobile)
			{
			printf("%s;%s;%s%d%s\n",
				name_int, buf2,
				get_index(phone_index),
				get_call_group(call_group,name_int),
				(opt_new_gnokii==(FLAG)0 ) ? ";" : ""
				) ;
			num_done = (FLAG)1 ;
			}
		}
	/* Print international format if wanted */
	if (num_international && !(num_done && num_one))
		{
		if (strnequ(buf1,prefix_international,ln_prefix_international) )
			/* buf1 is international */
			strcpy(buf2,buf1);
		else if strnequ(buf1,prefix_national,ln_prefix_national)
			{ /* buf1 is national */
			strcpy(buf2,int_prefix_country);
			strcat(buf2,buf1 + ln_prefix_national);
			}
		else	{ /* buf1 is local */
			strcpy(buf2,int_prefix_country);
			strcat(buf2,my_town);
			strcat(buf2,buf1);
			}
		/* Now print */
		if (opt_isdn_and_estic || opt_vcard)
			{
			if (opt_estic)
				/* Estic allows 16 chars in a name,
				   put person''s (short) name before
				   location */
				printf("%-30s\t\"%.13s %s\"\n",buf2,
					name_int,location);
			else
				/* Full name may run off screen,
					 so put location first */
				printf("%-30s\t\"%s %s\"\n",buf2,
					location, name_int);
			num_done = (FLAG)1 ;
			}
		else if (opt_mobile)
			{
			/* Convert "00" back to "+" */
			strcpy(buf1,"+");
			strcat(buf1,buf2 + ln_prefix_international);
			printf("%s;%s;%s%d%s\n", name_int, buf1,
				get_index(phone_index),
				get_call_group(call_group,name_int) ,
				(opt_new_gnokii==(FLAG)0 ) ? ";" : ""
				) ;
			num_done = (FLAG)1 ;
			}
		}
	return 0 ;
	}

	/* Trim any hashed trailing comments, then trim trailing white space */
	void
hash_trim( /* const */ char *ptr0)
	{
	char *ptr1 ;
	if (( ptr0 == (char *)0) || (*ptr0 == '\0')) return ;
	/* Trim any hashed trailing comments */
	if ((ptr1 = index(ptr0, (int)'#')) != NULL ) *ptr1 = '\0' ;
	/* Trim trailing white space */
	for (ptr1 = ptr0 ;	/* Warning: assignment discards ``const''
					from pointer target type */

		*ptr1++ != '\0' ; ) ;
	ptr1-- ;
	while (--ptr1 >= ptr0)
		{
		if ( isspace((int)*ptr1) ) *ptr1 = '\0' ;
		break ;
		}
	}

/* My phone book contained foreign language non ascii filth,
 *   from old German umlaut texts, & some other junk, eg
 *	Accent	Example				Hex
 *	'	I´m				B4
 *	A^					C2
 *	ae	zusätzlich			E4
 *	oe	möchten			F6
 *	e'	Spenlé			E9
 *	e'	Océ Printing Systems GmbH	E9
 *	UE	Diplom-Übersetzerin		DC
 *	ue	Glückwunsch			FC
 *	ss	Straße			DF
 *
 *   & I also had a back space (Control H) before the ' below
 *   (now here represented by BS)
 *	fn:M...,L....
 *	sn:G...(M), MaleBS'cot(L)
 *   Which produced this VCF
 *	FN:M...,L...e Grob(M), MaleBS'cot(L)
 *	N:G...(M), MaleBS'cot(L);M...,L...e;
 *	brackerts.c balancer: '
 *   That android phone app was happy importing,
 *   Tha Android app Fritz Fon was happy importing
 *   & exporting as a mail with .xml enclosure,
 *   but Fritz!Box 7490 failed on, while importing a .xml for a phone book.
 */
	void
force_ascii( /* const */ char *base)
	{
	char badc = '\0' ;
	char *cur, *badp ;
	FLAG nasty=0 ;
	if (( base == (char *)0) || (*base == '\0')) return ;
	for (cur = base ; *cur != '\0' ; cur++ )
		{
		if ( !isprint((int)*cur)
			&& ( *cur != '\t' ) && ( *cur != '\n' ) )
			{
			if ( badc == '\0' ) { badc = *cur ; badp = cur ; }
			*cur = '.' ;	// an easy match in vi when searching strings
			nasty=1 ;
			}
		}
	if (nasty) fprintf(stderr,"%s [%c] Hex [%x],\n%s [%s]\n%s %s\n\n",
		"Warning: One or more Non Ascii Characters, First is:",
		badc, (int)badc, "Bad String Starts:", badp,
		"Whole string forced to:", base);
	// -------------
	// JJLATER ',' is special to VCF. must be delimited,
	// currently I am not doing that in this C prog
	// but in a Makefile with sed - evil !
	// but sensible, at least till I learn what other
	// problems to also cater for.
	// -------------
	// Strip multiple nine to single lines, cos rolo refuse to import:
	// ADR;HOME:First address on first line.
	//          Second address indented on second line.
	for (cur = base ; *cur != '\0' ; cur++ )
	if ( *cur == '\n' ) 
		// *cur = '\0' ;	// Too savage.
		*cur = ' ' ;		// Less savage.
	}


/* For a lookup list, not for a full output,
   List all numbers {Telephone,Fax,Modem} x {Home, Work, Mobile} for a person
   Example output:
	01188		"Telefon Auskunft TW"	Phone Work
	001188		"Int. Auskunft TW"	Phone Work
	1234567		"Julian H. Stacey TW"	Phone Work
	2345678		"Julian H. Stacey FH"	Fax Home
	123456		"Fred Smith, IBM"
 */
	void
put_short(tmp_person)
	struct person *tmp_person ;
	{
	/* JJLATER maybe add some of tmp_person->psn_de to this procedure */
	char names[NAME_BIG] ;
	char *fn, *sn, *bn, *ie, *cg ;

	fn=tmp_person->psn_fn; sn=tmp_person->psn_sn;
	bn=tmp_person->psn_bn; ie=tmp_person->psn_ie;
	cg=tmp_person->psn_cg;
	hash_trim(fn); hash_trim(sn); hash_trim(bn);
	hash_trim(ie) ; hash_trim(cg) ;
	if (	((fn == (char *)0) || (*fn == '\0')) &&
		((sn == (char *)0) || (*sn == '\0')) &&
		((bn == (char *)0) || (*bn == '\0'))
		) return ;
	if ( opt_mobile &&
		(
		((ie == (char *)0) && ( opt_dflt_exclude == (FLAG)1)) ||
		((ie != (char *)0) && strequ (ie,"-" ) )
		)
	   )
		{
#ifdef DEBUG
		fprintf(stderr,"Skipping %s%s%s\n",
			(fn != (char *)0) ? fn : "" ,
			( ((fn != (char *)0) && (*fn != '\0'))	&&
			  ((sn != (char *)0) && (*sn != '\0')) ) ? " " : "" ,
			(sn != (char *)0) ?  sn : "" ,
			( ((sn != (char *)0) && (*sn != '\0'))	&&
			  ((bn != (char *)0) && (*bn != '\0')) ) ? "," : "" ,
			(bn != (char *)0) ? bn : ""
			) ;
#endif
		return ;
		}
	sprintf(names,"%.100s%s%.100s%s%.100s",
		/* Same 100 as NAME_LN */
		(fn != (char *)0) ? fn : "" ,
		( ((fn != (char *)0) && (*fn != '\0'))	&&
		  ((sn != (char *)0) && (*sn != '\0')) ) ? " " : "" ,
		(sn != (char *)0) ?  sn : "" ,
		( ((sn != (char *)0) && (*sn != '\0'))	&&
		  ((bn != (char *)0) && (*bn != '\0')) ) ?
							" "
		/* 27.08.2009 The preceeding  " " replaced a prior ",",
		   As option "-," now deprecates commas, so with the
		   previous comma in code, &
			This input:
				sn:Surnam
				bn:ABC Company
				cg:Business
				tw:+49.89.12345678.9
				ie:+
			+ this command:
				phone -g -w 14 -, -+ -n -1 ../../txt/contacts/firms.phone
			Produced this warning:
				phone Warning: ../../txt/contacts/firms.phone Converting Comma to Ampersand: Surnam,ABC CoW
			+ this output:
				Surnam ABC CoW;+4989123456789;SM;6;3
			 */
							    : "" ,
		(bn != (char *)0) ? bn : ""
		) ;

	(void)put_pair(tmp_person->psn_th,names,"TH",cg);
	(void)put_pair(tmp_person->psn_tw,names,"TW",cg);
	(void)put_pair(tmp_person->psn_tm,names,"TM",cg);
	(void)put_pair(tmp_person->psn_tn,names,"TN",cg);

	if (opt_isdn_and_estic || opt_vcard)	{ /* Only add "|| opt_mobile"
			if you need to store fax number in mobile */
		(void)put_pair(tmp_person->psn_fh,names,"FH",cg);
		(void)put_pair(tmp_person->psn_fw,names,"FW",cg);
		(void)put_pair(tmp_person->psn_fm,names,"FM",cg);
		}
	if (opt_isdn_and_estic || opt_vcard)	{ /* Only add "|| opt_mobile"
			 if you need to store modem number in mobile */
		(void)put_pair(tmp_person->psn_mh,names,"MH",cg);
		(void)put_pair(tmp_person->psn_mw,names,"MW",cg);
		(void)put_pair(tmp_person->psn_mm,names,"MM",cg);
		}
	if (opt_isdn_and_estic || opt_vcard)	{ /* Not useful to list isdn data connection
				numbers in mobile, so skip them
				for opt_mobile. */
		(void)put_pair(tmp_person->psn_ih,names,"IH",cg);
		(void)put_pair(tmp_person->psn_iw,names,"IW",cg);
		(void)put_pair(tmp_person->psn_im,names,"IM",cg);
		}
	}

char *mrs() { return "" ;
	/* JJLATER i could emit eg "Mr/s", but phone.c book currently
	 * contains no gender/sex differentiator field.
	 * GENDER is in http://tools.ietf.org/html/rfc6350#section-6.2.7
	 */
	}

	void
put_field(field,content)
	char *field, *content;
	{
	if ((content == (char *)0) 
		|| ( *content == '\0')
		// -------------
		// Rolo stops importing VCF on next record
		// after one with a blank eg
		//	URL;WORK:
		// produced by eg
		//	ww:
		// 	th:#    Ex +49.89.123456
		)  return ;
	if (!opt_vcard) 
		{
		printf("%s:%s\n",field,content);
		field_printed = 1 ;
		}
	else	{
		hash_trim(content);
		force_ascii(content);
		/* Next twice merge 2 or 3 single name fields (eg
		 *	phone:fn=firstname=Julian
		 *	phone:sn=surname=Stacey
		 *	phone:bn=business=My Company
		 * ) into two long output VCF vcard fields eg
		 *	FN:A single text value.
		 *  N:Structured text value.
		 *    Each component can have multiple values
		 * But both/all 3 fields not available in this
		 * procedure that accepts single fields at a time,
		 * so we have to store & flush later.
		 * Aim to emit:
		 *	FN: Julian H. Stacey\, My Company
		 *	N:My Company;Stacey;Julian H.
		 */
		if (strequ(field,"fn"))
			{sprintf(pers_fn,"%s",content);return;}
		else if (strequ(field,"sn"))
			{sprintf(pers_sn,"%s",content);return;}
		else if (strequ(field,"bn"))
			{sprintf(pers_bn,"%s",content);return;}
			// Add business name to caller ID.
			// Strictly it belongs in another field
			// (where I also put it) but I think its
			// useful showing in caller id.

		// Rely on the undocumented fact I always have some field
		// after all names, to trigger a merge of name components.

		if ( !pers_merged
			&& !strequ(field,"fn")
			&& !strequ(field,"sn")
			&& !strequ(field,"bn") &&
			( (pers_fn[0] != '\0') ||
			  (pers_sn[0] != '\0') ||
			  (pers_bn[0] != '\0') ) )
			{	// Merge names & emit FN & N
			pers_merged = 1;
			// -------------
			// Firm/Company/Business name doesnt properly
			// belong in N: or FN: fields of the VCF,
			// but I want it in to show in Android
			// & Fritz when it displays caller name.
			// -------------
			// FN:Full Names.
		// http://tools.ietf.org/html/rfc6350#section-6.2.1
			// FN:Mr. John Q. Public\, Esq.
			// FN:Julian H. Stacey
			// FN:Julian H. Stacey VSL
			printf("FN:%s%s%s%s%s\n", 
				pers_fn,
				( (pers_fn[0] != '\0') &&
				 ( (pers_sn[0] != '\0') ||
				   (pers_bn[0] != '\0') ) ) ? " " : "\0" ,
				pers_sn,
				( (pers_sn[0] != '\0') &&
				  (pers_bn[0] != '\0')) ? ", " : "\0" ,
				pers_bn
				);
			// ------------------------
			// A normal VCF field would be eg:
			//	N:Stacey;Julian;Howard;
			//	 Consultant BSc. Hons. Comp. & Cyb.
			// But I prepend company name too if present:
			//	N:VSL;Stacey;Julian;Howard;
			//	 Consultant BSc. Hons. Comp. & Cyb.
			// & occasionaly in my .phone books I include a
			// a pseudo bn: business name of eg: bn:FALL
			// That I can sort for (by business name aka function)
			// in android phone book, where FALL might be to
			// locate a friend to rescue a fallen friend, or eg
			// bn:CAR might be a list of eg car insurance,
			// service garage & tow truck rescue services etc. 
			printf("N:%s%s%s%s%s\n", pers_bn,
				( ( pers_bn[0] != '\0' ) &&
				( ( pers_sn[0] != '\0' ) || 
				     ( pers_fn[0] != '\0' ) ) ) ?  ";" : "\0" ,
				pers_sn,
				( ( pers_sn[0] != '\0' ) && 
				  ( pers_fn[0] != '\0' ) ) ? ";" : "\0" ,
				pers_fn );
			}
		if (strequ(field,"bn")) {printf("%s:%s\n",
			"ORG;TYPE=work",content);return;}
		if (strequ(field,"ac")) {printf("%s:%s\n",
			"CATEGORIES:Accom share ",content);return;}
		else if (strequ(field,"ah")) {printf("%s:%s\n",
			"ADR;HOME",content);return;}
		else if (strequ(field,"aw")) {printf("%s:%s\n",
			"ADR;WORK",content);return;}
			// http://tools.ietf.org/html/rfc6350#page-56
			// Shows: ADR;TYPE=work:
		else if (strequ(field,"bd")) {printf("%s:%s\n",
			"BDAY", /* content */ "Omitted for security" );
				/* VCF record are for both local
				 * use on Android phone app, & for
				 * conversion to .xml for Fritz
				 * router, using Fritz conversion
				 * app Fon on Android.
				 * Android is not secure, so dont
				 * let rogue apps harvest Date Of
				 * Birth.
				 */
			return;}

		else if (strequ(field,"bg")) {printf("%s:%s\n",
			"HOBBY:Beer Garden Regular ",content);return;}
		else if (strequ(field,"bo")) {printf("%s:%s\n",
			"HOBBY:Boat Owner ",content);return;}
		else if (strequ(field,"ca")) {printf("%s:%s\n",
			"HOBBY:Car ",content);return;}
		else if (strequ(field,"cg")) {printf("%s:%s\n",
			"CATEGORIES:Call Group ",content);return;}
		else if (strequ(field,"co")) {printf("%s:%s\n",
			"CATEGORIES:Context ",content);return;}
		else if (strequ(field,"cy")) {printf("%s:%s\n",
			"HOBBY:Cycle ",content);return;}
		else if (strequ(field,"de")) {printf("%s:%s\n",
			"ORG" //	"ORG:Department"
			,content);return;}
		else if (strequ(field,"eh")) {printf("%s:%s\n",
			"EMAIL;HOME",content);return;}
		else if (strequ(field,"em")) {printf("%s:%s\n",
			"EMAIL;CELL",content);return;}
		else if (strequ(field,"ew")) {printf("%s:%s\n",
			"EMAIL;WORK",content);return;}
		else if (strequ(field,"fh")) {printf("%s:%s\n",
			"TEL;TYPE=fax;HOME",content);return;}
		else if (strequ(field,"fm")) {printf("%s:%s\n",
			// "CATEGORIES:FAX CELL"
			"TEL;TYPE=fax;CELL",content);return;}
		else if (strequ(field,"fr")) {printf("%s:%s\n",
			"X-SPOUSE:Friend ",content);return;}
		else if (strequ(field,"fw")) {printf("%s:%s\n",
			"TEL;TYPE=fax;WORK",content);return;}
		else if (strequ(field,"ie")) {
			// printf("%s:%s\n",
			// "CATEGORIES:Include or Exclude",content);
			// JJLATER I should use this further out
			// to avoid printing entire vcard.
			return;}
		else if (strequ(field,"ih"))
			{printf("%s:%s\n",
			// "CATEGORIES:ISDN DATA HOME "
			"TEL;TYPE=textphone;HOME"
			,content);return;}
		else if (strequ(field,"im"))
			{printf("%s:%s\n",
			// "CATEGORIES:ISDN DATA CELL "
			"TEL;TYPE=textphone;CELL"
			,content);return;}
		else if (strequ(field,"iw"))
			{printf("%s:%s\n",
			// "CATEGORIES:ISDN DATA WORK "
			"TEL;TYPE=textphone;WORK"
			,content);return;}
		else if (strequ(field,"jo"))
			{printf("%s:%s\n",
			"CATEGORIES:Jobs ",content);return;}
		else if (strequ(field,"ld")) {	// Last Updated
			printf("%s:%s\n",
			// "REV"
			// I need to convert freeform text to rigorous
			// REV:20121201T134211Z
			"CATEGORIES: REV "
			,content);
			return;}
		else if (strequ(field,"mh"))
			{printf("%s:%s\n",
			// "CATEGORIES:MODEM HOME"
			"TEL;TYPE=video;HOME"
			,content);return;}
		else if (strequ(field,"mm"))
			{printf("%s:%s\n",
			// "CATEGORIES:MODEM CELL"
			"TEL;TYPE=video;HOME"
			,content);return;}
		else if (strequ(field,"mw"))
			{printf("%s:%s\n",
			// "CATEGORIES:MODEM WORK"
			"TEL;TYPE=video;WORK"
			,content);return;}
		else if (strequ(field,"na"))
			{printf("%s:%s\n",
			"CATEGORIES:Nationality ",content);return;}
		else if (strequ(field,"oc"))
			{printf("%s:%s\n","TITLE",content);return;}
			// ROLE is also available

		else if (strequ(field,"sk"))
			{printf("%s:%s\n",
			"HOBBY:Skier ",content);return;}
		else if (strequ(field,"sm"))
			{printf("%s:%s\n",
			"CATEGORIES:Smoker ",content);return;}
		else if (strequ(field,"te"))
			{printf("%s:%s\n",
			"HOBBY:Tent Owner ",content);return;}
		else if (strequ(field,"th"))
			{printf("%s:%s\n","TEL;HOME",content);return;}
			// "TEL;TYPE=\"voice,home"\"
			// http://tools.ietf.org/html/rfc6350
		else if (strequ(field,"tm"))
			{printf("%s:%s\n","TEL;CELL",content);return;}
			// "TEL;TYPE=cell"
			// "TEL;TYPE=voice;TYPE=text"
			// http://tools.ietf.org/html/rfc6350
		else if (strequ(field,"tn"))
			{printf("%s:%s\n","TEL;Unknown",content);return;}
		else if (strequ(field,"tw"))
			{printf("%s:%s\n","TEL;WORK",content);return;}
		else if (strequ(field,"tx"))
			{printf("%s:%s\n",
			"CATEGORIES:Telex ",content);return;}
		else if (strequ(field,"wh"))
			{printf("%s:%s\n",
			"URL;HOME",content);return;}
			// http://tools.ietf.org/html/rfc6350#page-56
			// shows URL;TYPE=home:
		else if (strequ(field,"ws"))
			{printf("%s:%s\n",
			"HOBBY:Windsurfer",content);return;}
		else if (strequ(field,"ww"))
			{printf("%s:%s\n","URL;WORK",content);return;}
		else if (strequ(field,"xr"))
			{printf("%s:%s\n",
			"CATEGORIES:Xmas Card Received ",content);return;}
		else if (strequ(field,"xs"))
			{printf("%s:%s\n",
			"CATEGORIES:Xmas Card Sent ",content);return;}
		else if (strequ(field,"cz"))
			{printf("%s:%s\n", "NOTE", content ); return;}
		else if (strequ(field,"zz")) {
			// Android insecure.
			// Never export secret PIN numbers etc.
			return;}
		printf(
		  "ERROR___ phone.c Unrecognised field type %s:%s\n",
			field,content);
		}
	field_printed = 1 ;
	}

	void
put_person(pp)
	struct person *pp ;
	{
	field_printed = 0 ;

	/* LATER add upper case conversion */

	if (opt_vcard)
		{
		// Omit contacts with no names.
		// So far I am only creating .vcf for Android phone app,
		// not eg for email apps.
		if (	/* No names. */
			( pp->psn_fn == '\0' ) &&
			( pp->psn_sn == '\0' ) &&
			( pp->psn_bn == '\0' ) ) return ;

		// Omit contacts with no phone numbers.
		// So far I am only creating .vcf for Android phone app,
		// I only use android contacts called from the phone app
		// & I have a lot of records in my firms.phone file that
		// dont have phone numbers, athat I want to strip
		// to squeee my business phone book to less than
		// 1000 to fit the Fritz .xml limitation.
		if (	/* No numbers. */
			( pp->psn_th == '\0' ) &&
			( pp->psn_tw == '\0' ) &&
			( pp->psn_tm == '\0' ) &&
			( pp->psn_tn == '\0' ) &&

			( pp->psn_fh == '\0' ) &&
			( pp->psn_fm == '\0' ) &&
			( pp->psn_fw == '\0' ) &&

			( pp->psn_ih == '\0' ) &&
			( pp->psn_iw == '\0' ) &&
			( pp->psn_im == '\0' ) &&

			( pp->psn_mh == '\0' ) &&
			( pp->psn_mw == '\0' ) &&
			( pp->psn_mm == '\0' ) ) return ;

		if ( contacts_max > 0 )
			{
			if (++contacts_count > contacts_max )
				{
				fprintf(stderr,"Warning: Would have exceeded %u VCF records.\n",contacts_max);
				exit(1);
				}
			}

		printf( "BEGIN:VCARD\nVERSION:2.1\n"	);
		// List of version numbers:
		// 2.1 Android phone app generates this.
		// 3.0 http://tools.ietf.org/html/rfc2426#page-25
		//	vCard MIME Directory Profile
		// 2.0 http://tools.ietf.org/html/rfc5545#page-80
		//	Internet Calendaring and Scheduling
		//	Core Object Specification
		// 4.0 http://tools.ietf.org/html/rfc6350#page-23
		//	vCard Format Specification
		// 4.0 http://tools.ietf.org/html/rfc6351#page-3
		//	xCard: vCard XML Representation

		pers_merged = 0 ;
		pers_fn[0] = pers_sn[0] = pers_bn[0] = '\0' ;
		}

	/* Contact Info */

	put_field("fn",pp->psn_fn);	/* Forenames			*/
	put_field("sn",pp->psn_sn);	/* Surname			*/

	put_field("bn",pp->psn_bn);	/* Business Name		*/
	put_field("de",pp->psn_de);	/* Department Name		*/

	put_field("th",pp->psn_th);	/* Telephone home		*/
	put_field("tw",pp->psn_tw);	/* Telephone work		*/
	put_field("tm",pp->psn_tm);	/* Telephone mobile		*/
	put_field("tn",pp->psn_tn);	/* Telephone not known where	*/

	put_field("ie",pp->psn_ie);	/* Include/Exclude in own mobile */

	put_field("fh",pp->psn_fh);	/* Fax Home			*/
	put_field("fm",pp->psn_fm);	/* Fax Mobile			*/
	put_field("fw",pp->psn_fw);	/* Fax Work			*/

	put_field("mh",pp->psn_mh);	/* Modem home			*/
	put_field("mw",pp->psn_mw);	/* Modem work			*/
	put_field("mm",pp->psn_mm);	/* Modem mobile			*/

	put_field("ih",pp->psn_ih);	/* ISDN Home			*/
	put_field("iw",pp->psn_iw);	/* ISDN Work			*/
	put_field("im",pp->psn_im);	/* ISDN Mobile			*/

	put_field("eh",pp->psn_eh);	/* Email home			*/
	put_field("ew",pp->psn_ew);	/* Email work			*/
	put_field("em",pp->psn_em);	/* Email mobile			*/

	put_field("wh",pp->psn_wh);	/* Web Home (personal)		*/
	put_field("ww",pp->psn_ww);	/* Web Work (business)		*/

	put_field("ah",pp->psn_ah);	/* Address home			*/
	put_field("aw",pp->psn_aw);	/* Address work			*/

	put_field("tx",pp->psn_tx);	/* Telex			*/

	/* Occupation/Business Info */

	put_field("oc",pp->psn_oc);	/* Occupation			*/
	put_field("jo",pp->psn_jo);	/* Jobs, last approach YYYY.MM.DD */

	put_field("cg",pp->psn_cg);	/* Call Group for incoming	*/
	put_field("co",pp->psn_co);	/* Context EG vsl ukc bg	*/

	/* Leisure Info */

	put_field("bg",pp->psn_bg);	/* Beer Garden Regular		*/
	put_field("cy",pp->psn_cy);	/* Cycle			*/
	put_field("sk",pp->psn_sk);	/* Skier			*/
	put_field("ws",pp->psn_ws);	/* Windsurfer			*/

	/* Equipment */

	put_field("bo",pp->psn_bo);	/* Boat (rubber inflatable)	*/
	put_field("ca",pp->psn_ca);	/* Car				*/
	put_field("te",pp->psn_te);	/* Tent				*/

	/* Personal Info */

	put_field("ac",pp->psn_ac);	/* Accom share			*/
	put_field("bd",pp->psn_bd);	/* Birth date			*/
	put_field("fr",pp->psn_fr);	/* Friend			*/
	put_field("na",pp->psn_na);	/* Nationality ie GB, D, F	*/
	put_field("sm",pp->psn_sm);	/* Smoker			*/
	put_field("xr",pp->psn_xr);	/* Xmas Card Received		*/
	put_field("xs",pp->psn_xs);	/* Xmas Card Sent		*/

	/* Odd Comments Last */

	put_field("cz",pp->psn_cz); /* Exported Comment (all else)	*/

	// put_field("zz",pp->psn_zz); /* Secret Comment (all else)	*/
	// Later if I extend this code to output in original format
	// then I could make an exception & export,
	// but never export to insecure devices such as android.

	put_field("ld",pp->psn_ld);	/* Last update			*/

	if (opt_vcard)
		{
		printf("END:VCARD\n");
		printf( "\n"	);	// Easier to read output,
			// dont know if rfc compliant,
			// but android phone app is happy to import with it.
		}

	if ((!( opt_isdn_and_estic || opt_estic || opt_mobile || opt_vcard))
		&& field_printed ) printf("%s\n",seperator);
	}

	void
put_file()
	{
	cur_person = first_person ;
	while (cur_person != (struct person *)0)
		{
		if (opt_isdn_and_estic || opt_mobile)
			{
			put_short(cur_person) ;
			}
		else	put_person(cur_person) ;
		cur_person = cur_person->psn_next ;
		}
	}

	void
sort_file()
	{
	/* struct person *low, *high, *tmp ; */
	return; /* JJLATER algorithm incomplete */
#if 0	/*{*/
	low = first_person ;
	while ((low != (struct person *)0) &&
		((high = low->psn_next) != (struct person *)0))
		{
		if (!(opt_isdn_and_estic || opt_vcard || opt_mobile))
			{ /* Sort by first name.
				Perhaps later I might sort by surname then by
				forname, (so all members of a family get
				listed together), but that wouldnt be useful
				using mobile phone.
				JJLATER Maybe I should sort by
				bn: business name ?
			 */
			if (strcmp(low->psn_fn,high->psn_fn))
				{ tmp = low ; low = high ; high = tmp ; }
			}
		else
			{ /* Not much point,
				as pipe through sort will be better */
			if (strcmp(low->psn_th,high->psn_th))
				{ tmp = low ; low = high ; high = tmp ; }
			}
		low = low->psn_next ;
		}
#endif	/*}*/
	}

	void
do_file()
	{
	get_file() ;
	if (opt_sort) sort_file() ;
	put_file() ;
	}

	void
syntax()
	{
	fprintf(stderr,
	 "Syntax: %s %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
		*ARGV,
		"[-+] ",
		"[-,] ",
		"[-1] ",
		"[-?] ",
		"[-C] ",
		"[-G 0] ",
		"[-G 1] ",
		"[-I international_prefix] ",
		"[-L] ",
		"[-N national_prefix] ",
		"[-S] ",
		"[-c country_number] ",
		"[-g] ",
		"[-i] ",
		"[-l] ",
		"[-m] ",
		"[-n] ",
		"[-p phone_records] ",
		"[-b vcf cards per book] ",
		"[-s sim_records] ",
		"[-t town_number] ",
		"[-w name_width] ",
		"[-z] ",

		"-- or File[s]"
		) ;
	fprintf(stderr,"Valid keys are: ");
	print_keys() ;
	}

	void
digit(char *str)
	{
	int dummy_int;
	if (sscanf(str,"%d",&dummy_int) != 1 )
		/* sscanf is an inadequate test */
		{
		fprintf(stderr,
			"Fatal Error, Prefix should be numeric: \"%s\"\n",
			str);
		syntax();
		exit(1);
		}
	}

	int
main(argc, argv)
	int	argc ; char	**argv ;
	{
	int	exit_code =	0 ;
	char	*p ;
	ARGV =	argv ;
#ifdef	VSL	/* { */
#include	"../../include/vsl.h"
#endif		/* } */
	strcpy(my_country,		 MY_COUNTRY );
	strcpy(my_town,			 MY_TOWN );
	strcpy(prefix_international,	 PREFIX_INTERNATIONAL );
	strcpy(prefix_national,		 PREFIX_NATIONAL );

	for (argc--, argv++ ; argc ; argv++, argc--)
		{
		if (**argv != '-') break ;
		p = *argv + 1 ;
		while(*p) switch(*p++) /* Thus allow "-esz" as well as -e -s */
			{
			case '+': /* Exclude entries lacking an "ie:+". */
				opt_dflt_exclude = (FLAG)1 ;
				break ;
			case '1': /* Just one number */
				num_one = (FLAG)1 ;
				break ;
			case '?' :
				syntax() ;
				exit(0) ;
				break ;
			case 'c' : /* "1" for USA, 44 for Britain etc */
				if (--argc <= 0) syntax() ;
				strcpy(my_country, *++argv);
				break ;
			case 'g': /* Produce gnokii table name:number */
				opt_mobile = (FLAG)1 ;
				break ;
			case 'V': /* Produce gnokii table name:number */
				opt_vcard = (FLAG)1 ;
				break ;
			case ',': /* If set, & if opt_mobile also set,
					convert comma in names to ampersand */
				opt_comma = (FLAG)1 ;
				break ;
			case 'G': /* Produce gnokii table name:number */
				if (--argc <= 0) syntax() ;
				if ((unsigned)atoi(*++argv) == (FLAG)0)
					opt_new_gnokii = (FLAG)0 ;
				else opt_new_gnokii = (FLAG)1 ;
				opt_mobile = (FLAG)1 ;
				break ;
			case 'i': /* No international number */
				num_international = (FLAG)0 ;
				break ;
			case 'l': /* Produce lookup table numbers:names */
				opt_isdn_and_estic = (FLAG)1 ;
				break ;
			case 'm': /* No local number */
				num_local = (FLAG)0 ;
				break ;
			case 'n': /* No national variant number */
				num_national = (FLAG)0 ;
				break ;
			case 'p' : /* Phone memory, max number of names */
				if (--argc <= 0) syntax() ;
				phone_max = (unsigned)atoi(*++argv);
				break ;
			case 'b' : /* max contacts per vcf book */
				if (--argc <= 0) syntax() ;
				contacts_max = (unsigned)atoi(*++argv);
				break ;
			case 's' : /* Sim memory, max. number of names */
				if (--argc <= 0) syntax() ;
				sim_max = (unsigned)atoi(*++argv);
				break ;
			case 't' : /* "89" for Munich, by default */
				if (--argc <= 0) syntax() ;
				strcpy(my_town, *++argv);
				break ;
			case 'w' : /* Phone max name length */
				if (--argc <= 0) syntax() ;
				name_width = (unsigned)atoi(*++argv);
				if (( name_width > NAME_WIDTH_MAX) ||
					( name_width < NAME_WIDTH_MIN))
					{
					fprintf(stderr,
						"-w must be between %d & %d\n",
						NAME_WIDTH_MIN,
						NAME_WIDTH_MAX);
					exit(1);
					}
				break ;
			case 'z': /* Zap unlikely short numbers */
				opt_zap = (FLAG)1 ;
				break ;
			case 'C' : /* Capitalise whole name for small screens */
				opt_capital = (FLAG)1 ;
				break ;
			case 'I' : /* "00" for international prefix */
				if (--argc <= 0) syntax() ;
				digit(*++argv);
				strcpy(prefix_international, *argv);
				break ;
			case 'L': /* Restrict output name for estic */
				opt_isdn_and_estic = opt_estic = (FLAG)1 ;
				break ;
			case 'N' : /* "0" for national trunk prefix */
				if (--argc <= 0) syntax() ;
				digit(*++argv);
				strcpy(prefix_national, *argv);
				break ;
			case 'S': /* Sort output */
				opt_sort = (FLAG)1 ;
				break ;
			default:
				fprintf(stderr, "Unknown flag %c\n", *(p-1)) ;
				syntax();
				exit(1);
				break ;
			}
		}
	if (opt_mobile) {
		if (
		     (
			( num_international == (FLAG)0 ) &&
			( num_national == (FLAG)0 )
		     ) ||
		     (
			( num_international == (FLAG)1 ) &&
			( num_national == (FLAG)1 )
		     )
		   )	{
			syntax();
			fprintf(stderr,
			"Forcing International rather than national numbers\n");
			num_international = (FLAG)1 ;
			num_national = (FLAG)0 ;
			}
		/* We do not test for num_local as some local EG "112"
			need to be allowed through &
			not swapped to "+4989112" */
		}
	if ((opt_vcard) && (opt_mobile || opt_isdn_and_estic ) )
		{
		syntax();
		fprintf(stderr, "Error, cannot use both -V and -g\n");
		exit(1);
		}
	if (!( num_local || num_national || num_international ))
		{
		syntax();
		fprintf(stderr, "Error, cannot use all of -i -n -m\n");
		fprintf(stderr, "Forcing International numbers\n");
		num_international = (FLAG)1 ;
		}
	if ((opt_dflt_exclude == (FLAG)1 ) && (opt_mobile == (FLAG)0 ))
		{
		syntax();
		fprintf(stderr, "Warning: -+ is meaningless without -g.\n");
		}

	seperator_ln = strlen(seperator);
	person_ln = sizeof(struct person) ;
	sprintf(int_prefix_country,"%s%s",prefix_international,my_country);
	sprintf(nat_prefix_town,"%s%s",prefix_national,my_town);
	ln_int_prefix_country = strlen(int_prefix_country);
	ln_nat_prefix_town = strlen(nat_prefix_town);
	ln_prefix_national = strlen(prefix_national);
	ln_prefix_international = strlen(prefix_international);
	if (argc == 0)
		{
		file_name = "stdin" ;
		fp_in = stdin ;
		do_file() ;
		exit(0) ;
		}
	else do {
		file_name = *argv++ ;
		fp_in = fopen(file_name, "r") ;
		if (fp_in == (FILE *)0)
			{
			fprintf(stderr, "Cannot open '%s'\n",file_name) ;
			exit_code |= EXIT_FAILURE ;
			continue ;
			}
		do_file() ;
		(void) fclose(fp_in) ;
		} while (--argc) ;
	exit(exit_code) ;
	}
