/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *  This file is part of jcabc2ps,
 *  Copyright (C) 1996,1997,1998  Michael Methfessel
 *  See file jcabc2ps.c for details.
 */

#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include "jcabc2ps.h"
#include "util.h"
#include "format.h"
#include "subs.h"
#include "text.h"
#include "misc.h"

#include "parse.h"

int syntaxErrors = 0;	// Count the error messages
int errorLimit = 100;	// Give up after this many errors

int middleadj (char note);

int ksigcancel = 0;		// Flag controlling ksig-cancel code.
Ksig *lastks = 0;		// For remembering Ksig struct

struct clef_desc {
	char *name;
	int val;
} clefname[] = {		// We do case-insensitive compares
	{"TREBLE",	0},
	{"TREBLE",   10},
	{"TREBLE-8", 11},	// Treble-8
	{"TREBLE+8", 12},	// Treble+8
	{"ALTO",	 20},
	{"ALTO-8",   21},	// Alto-8
	{"ALTO+8",   22},	// Alto+8
	{"TENOR",	13},	// Tenor = Treble-8
	{"BASS",	 30},
	{"BASS-8",   31},	// Bass-8
	{"BASS+8",   32},	// Bass+8
	{"NOCLEF",   90},	// Don't show clef at all
	{"NOCLEF-8", 91},	// These are for completeness,
	{"NOCLEF+8", 92},	// but we don't expect to see them.
	{0,0}
};
char *SymClef(n)
{	int i;
	for (i=0; clefname[i].name; i++) {
		if (clefname[i].val == n) {
			return clefname[i].name;
		}
	}
	return "";
}

char *symname[] = {
	"[0]",			/*  0 */
	"INVISIBLE",	/*  1 valid symbol types */
	"NOTE",			/*  2 */
	"REST",			/*  3 */
	"BAR",			/*  4 */
	"CLEF",			/*  5 */
	"TIMESIG",		/*  6 */
	"KEYSIG",		/*  7 */
	"ACHORD",		/*  8 */
	"KEYSGN",		/*  9 */
	"ANNOT",		/* 10 */
	"[11]",			/* 11 */
	"[12]",			/* 12 */
	"[13]",			/* 13 */
	"[14]",			/* 14 */
	"[15]",			/* 15 */
	"[16]",			/* 16 */
};
char * SymName(n)
{	if (n < 0 || n > 16) return "UNKNOWN";
	return symname[n];
}

int note_pos( char note, int clef);

int   achp = 0;		/* Position (above, below) of current accompaniment chord */

int   decos = 0;	/* Number of decorations on current symbol */
int   dtype[30];	/* Decoration types */
int   oldks = 0;	/* Whether to use old keysig code */
int   unexpsym = 0;	/* Line number for "Unexpected symbol" messages */

/*
* for each note A..G, these tables tell how many sharps (resp. flats)
* the keysig must have to get the accidental on this note. Phew.
*/
int  sh_tab[] = {5,7,2,4,6,1,3};	// Position of sharps on treble staff.
int  fl_tab[] = {3,1,6,4,2,7,5};	// Position of  flats on treble staff.
char sh_val[] = {'f','c','g','d','A','e','B'};
char fl_val[] = {'B','e','A','d','G','c','F'};

/*  subroutines connected with parsing the input file  */

void clearDecos (void)
{	char *F ="clearDecos";

	V3 "%s: DECOs cleared out\n",F V;
	decos = 0;
}

void addDeco(
	int   deco_type)
{	char *F ="addDeco";
//
	if (decos < 30) {
		V5 "%s: DECO %d is type %d.\n",F,decos,deco_type V;
		dtype[decos++] = deco_type;
	} else {
		V5 "%s: DECO buffer full, skipping DECO of type %d.\n",F,deco_type V;
	}
}

void copyDecosToSym(
	Sym *sp)
{
	char *F ="copyDecosToSym";
	int i;

	V3 "%s: Copying DECOs to symbol\n",F V;
	for (i = 0;i<decos;i++) {
		sp->dc.t[i] = dtype[i];
		V5 "%s: decoration %d is type %d.\n",F,i,sp->dc.t[i] V;
	}
	sp->dc.n = decos;		/* Decoration count */
}

/* ----- sytax: print message for syntax errror -------- */
void syntax(
	char *msg,
	char *q)
{	char *F="syntax";
	int i,n,len,m1,m2,pp,qq,maxcol = 65;

	V6 "%s: Called for msg=\"%s\" q=\"%s\"\n",F,(msg?msg:""),(q?q:"") V;
	if (verbose <= 2) fprintf(stderr,"\n");
	unless (Q) Q= q;
	qq = q-Q+1;
	if (qq<0) qq = 0;
	fprintf(stderr,"+++ %s in line %d.%d tune %d.\n", msg, linenum, qq, xrefnum);
	m1 = 0;
	m2 = len = strlen(Q);
	n = q-Q;
	if (m2>maxcol) {
		if (n<maxcol)
			m2 = maxcol;
		else {
			m1 = n-10;
			m2 = m1+maxcol;
			if (m2>len) m2 = len;
		}
	}

	fprintf(stderr,"%4d ", linenum);
	pp = 5;
	if (m1>0) { fprintf(stderr,"..."); pp += 3; }
	for (i = m1;i<m2;i++) fprintf(stderr,"%c", Q[i]);
	if (m2<len) fprintf(stderr,"...");
	fprintf(stderr,"\n");

	if (n >= 0 && n<200) {
		for (i = 0;i<n+pp-m1;i++) fprintf(stderr," ");
		fprintf(stderr,"^\n");
	}
	++syntaxErrors;
}

/* ----- isnote: checks char for valid note symbol ----- */
int isnote (char c)
{
	if (c == '\0') return 0;
	if (strchr("CDEFGABcdefgab^ = _",c)) return 1;
	return 0;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Zero out a symbol.  First, we must check the pointers, and free the things
* that they point to.  Then we fill the entire struct with zeroes.  Finally,
* we stick in a few nonzero defaults values.
*/
void zeroSym (
	Sym  *s,
	char *d)
{	char *F = "zeroSym";
	int j;
	V3 "%s: Zero   sym %08X \"%s\"...\n",F,(uint)s,d V;
	if (s->ksig) {
		V3 "%s: Free s->ksig for \"%s\"\n",F,d V;
		Free(s->ksig,"Ksig");
		V6 "Forget s->ksig=%08X",(uint)s->ksig V;
		s->ksig = 0;
	}
	if (s->ann) {
		V3 "%s: Free s->ann for \"%s\"\n",F,d V;
		zapText(s->ann,"ann");
		V6 "Forget s->ann=%08X",(uint)s->ann V;
		s->ann = 0;
	}
//	for (j = 0; j < NWLINE; j++ ) {
//		if (s->wordp[j]) {
//			V3 "%s: Free s->wordp[%d] for %d\n",F,j,d V;
//			Free(s->wordp[j],"wordp");	// This isn't allocated.
//			s->wordp[j] = 0;
//		}
//	}
	bzero(s,sizeof(Sym));
	s->ylo	   = 12;
	s->yhi	   = 12;
	s->p		 = -1;
	V3 "%s: Zeroed sym %08X \"%s\".\n",F,(uint)s,d V;
}

/* ----- add_sym: returns index for new symbol at end of list ---- */
int add_sym (type)
	int type;
{	char *F = "add_sym";
	int k;
	V6 "%s: Called for sym %d=%s\n",F,type,symname[type] V;
	k = voice[ivc].nsym;
	if (k >= maxSyms) rxi("Too many symbols; increase maxSyms, now ",maxSyms);
	voice[ivc].nsym++;
	zeroSym(&symv[ivc][k],"symv/add_sym");
	symv[ivc][k].type = type;
	if (!symv[ivc][k].ksig) {
		symv[ivc][k].ksig = newKsig(F);
		V6 "%s: Copied new Ksig %X to symv[%d][%d].ksig\n",F,(uint)symv[ivc][k].ksig,ivc,k V;
	}
//	TextInit(&ann,64);
	return k;
}

/* ----- insert_sym: returns index for new symbol inserted at k --- */
int insert_sym (type,k)
	int type,k;
{
	int i,n;

	n = voice[ivc].nsym;
	if (n >= maxSyms) rxi("insert_sym: maxSyms exceeded: ",maxSyms);
	for (i = n; i > k; i--) {
		symv[ivc][i] = symv[ivc][i-1];
	}
	n++;
	zeroSym(&symv[ivc][k],"symv/insert_sym");
	symv[ivc][k].type = type;
	voice[ivc].nsym = n;
	return k;
}

/* ----- get_xref: get xref from string ----- */
int get_xref (str)
char str[];
{

	int a,ok;
	char *q;

	if (strlen(str) == 0) {
		wng("xref string is empty", "");
		return 0;
	}
	q = str;
	ok = 1;
	while (*q != '\0') {
		if (!isdigit(*q)) ok = 0;
		q++;
	}
	if (!ok) {
		wng ("xref string has invalid symbols: ", str);
		return 0;
	}

	sscanf (str, "%d", &a);

	return a;
}

/* ----- set_meter: interpret meter string, store in struct ---- */
void set_meter (str,meter)
	char *str;
	Msig *meter;
{	char *F="set_meter";
	int   m1=0, m2=0, m1a=0, m1b=0, m1c=0, d=0, l=0;
	char *q;
	int   meter1=0, meter2=0, dlen=0, mflag=0, lflag=0;
	char  meter_top[31];

	l = strlen(str);
	if (l == 0) {
		V3 "Null meter.\n" V;
		meter1 = meter2 = 4;
		dlen = EIGHTH;
		mflag = 4;
		strcpy(meter_top,"-");
	} elsif (!strcasecmp(str,"none")) {
		V3 "Free meter.\n" V;
		meter1 = meter2 = 4;
		dlen = EIGHTH;
		mflag = 4;
		strcpy(meter_top,"-");
	} elsif (str[0] == 'C') {
		if (str[1] == '|') {
			V3 "Common meter.\n" V;
			meter1 = meter2 = 4;
			dlen = EIGHTH;
			mflag = 2;
			strcpy(meter_top,"C");
		} else {
			V3 "Cut meter.\n" V;
			meter1 = meter2 = 4;
			dlen = EIGHTH;
			mflag = 1;
			strcpy(meter_top,"C");
		}
	} else {
		strcpy (meter_top, str);
		q = strchr(meter_top,'/');
		if (!q) {
			wng("Cannot identify meter, missing /: ", str);
			m1 = m2 = 1;
			return;
		}
		*q++ = '\0';
		if (strchr(meter_top,'+')) {
			sscanf(str,"%d+%d+%d/", &m1a, &m1b, &m1c);
			V2 "%s; '+' m1a=%d m1b=%d m1c=%d\n",F,m1a,m1b,m1c V;
			m1 = m1a + m1b + m1c;
		} else {
			sscanf(str,"%d %d %d/", &m1a, &m1b, &m1c);
			V2 "%s; ddd/ m1a=%d m1b=%d m1c=%d\n",F,m1a,m1b,m1c V;
			m1 = m1a;
			if (m1b>m1) m1 = m1b;
			if (m1c>m1) m1 = m1c;
			if (m1>30) {			/* handle things like 78/8 */
				m1a = m1 / 100;
				m1c = m1 - 100 * m1a;
				m1b = m1c / 10;
				m1c = m1c - 10 * m1b;
				m1 = m1a;
				if (m1b>m1) m1 = m1b;
				if (m1c>m1) m1 = m1c;
			}
		}
		while (*q == '/') q++;	// [jc] We sometimes see repeated /
		sscanf (q, "%d", &m2);
		V2 "%s; q=\"%s\" m1a=%d m1b=%d m1c=%d m2=%d\n",F,q,m1a,m1b,m1c,m2 V;
		if (m1*m2 == 0) {
			V1 "Cannot identify meter: \"%s\" (m1=%d m2=%d q=\"%s\")\n",str,m1,m2,q V;
			if (!m1) m1 = 1;
			if (!m2) m2 = 1;
		}
		d = BASE/m2;
		if (d*m2 != BASE) {
			V2 "Warning: Meter not recognized: \"%s\" (BASE=%d m1=%d m2=%d d=%d)\n",str,BASE,m1,m2,d V;
		}
		meter1 = m1;
		meter2 = m2;
		dlen = EIGHTH;
		if (4*meter1 < 3*meter2) dlen = SIXTEENTH;
		mflag = 0;
	}
	V3 "%s: Meter <%s> is %d over %d with default length 1/%d\n",F,str,meter1,meter2,BASE/(dlen?dlen:1) V;

	/* handle old-style change of default length */
//	lflag = 0;
	if (str[l-1] == 's') { dlen = dlen*2; lflag =  1; }
	if (str[l-1] == 'l') { dlen = dlen/2; lflag = -1; }

	/* store parsed data in struct */
	meter->meter1 = meter1;
	meter->meter2 = meter2;
	meter->mflag  = mflag;
	if (!meter->dlen) {			// Set the default length only if not yet known.
		V3 "%s: Change meter->dlen from %d to %d.\n",F,meter->dlen,dlen V;
		meter->dlen  = dlen;
	}
	meter->lflag  = lflag;
	strcpy(meter->top, meter_top);
}


/* ----- set_dlen: set default length for parsed notes ---- */
void set_dlen (str,meter)
	char str[];
	Msig *meter;
{	char *F = "set_dlen";
	int l1,l2,d,dlen;

	l1 = 0;
	l2 = 1;
	sscanf(str,"%d/%d ", &l1, &l2);
	if (l1 == 0) {
		return;	   /* empty string.. don't change default length */
	} else {
		d = BASE/l2;
		if (d*l2 != BASE) {
			wng("Length incompatible with BASE, using 1/8: ",str);
			dlen = BASE/8;
		} else {
			dlen = d*l1;
		}
	}
	V3 "%s: <%s> sets default note length to %d/%d = 1/%d\n",F,str,dlen,BASE,BASE/dlen V;
	meter->dlen = dlen;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Caseless string comparison.  This was done so that we can insert debug info,
* which isn't feasible with the library strcasecmpn() routine. N.B.: This code
* converts both strings to lower case.  As far  as  I  can  tell,  this  isn't
* actually  a  problem  in any of the existing calls.  But I thought a warning
* might be in order here of an unexpected side-effect.  [jc 2014-12-21]
*/
int nocasecmpn(s1,s2,n)
	char *s1, *s2;
	int   n;
{	char *F="nocasecmpn";
	char *p1, *p2;
	int   c1,  c2, i;
//	V8 "%s: n=%d \"%s\" \"%s\"\n",F,n,s1,s2 V;
	for (i=0; i<n; i++) {
		c1 = tolower(s1[i]);
		c2 = tolower(s2[i]);
		if (c1 == 0 && c2 == 0) {	// They're equal
//			V8 "%s: Return 0 at char %d ('%c','%c')\n",F,i,c1,c2 V;
			return 0;
		}
		if (c1 == 0) {			// s2 is greater
//			V8 "%s: Return %d at char %d ('%c','%c')\n",F,i+1,i,c1,c2 V;
			return   i+1;
		}
		if (c2 == 0) {			// s1 is greater
//			V8 "%s: Return %d at char %d ('%c','%c')\n",F,-(i+1),i,c1,c2 V;
			return -(i+1);
		}
		if (c1 < c2) {			// s2 is greater
//			V8 "%s: Return %d at char %d ('%c','%c')\n",F,i+1,i,c1,c2 V;
			return   i+1;
		}
		if (c1 > c2) {			// s1 is greater
//			V8 "%s: Return %d at char %d ('%c','%c')\n",F,-(i+1),i,c1,c2 V;
			return -(i+1);
		}
	}
//	V8 "%s: Return 0 at char %d (EQUAL)\n",F,i,c1,c2 V;
	return 0;
}

/* ----------- parseKey: interpret keysig string, store in struct -----------
*  This part was adapted from abc2mtex by Chris Walshaw updated 03  Oct  1997
*  Wil  Macaulay - support all modes Returns 1 if key was actually specified,
*  because then we want to transpose.  Returns zero if this is  just  a  clef
*  change.
*
* Modified by John Chambers to allow for accidentals after the tonic and mode
* fields.   The  result  is  a  Ksig  struct  in  the  symbol  that lists the
* accidentals  explicitly.   This  could  be  an  implementation  of  "global
* accidentals" if we were following the 1.6 standard, or it could be used for
* arbitrary key signatures, as in the newer standard.
*/
int parseKey(s,ks,init)
	char *s;
	Ksig *ks;
	int   init;
{	char *F = "parseKey";
	int   a = 0;
	int   accs = 0;
	int   c = 0;		// Current char in s
	int   c1, c2;		// Assorted chars
	int   i, j, k, l;	// Assorted ints
	int   ok = 0;
	int   sf = 0;		// Sharps or flats for classical key signatures.
	int   tonic = 'C';	// Tonic note
	int   tacc = ' ';	// Tonic accidental
	char  w[81];		// Holds one "word" from line
	char *eksp = 0;		// Start of explicit key signature
	int   clef = 0;		// Clef code
	int   middle;		// Middle note of staff
	int   gotmiddle=0;	// True if middle has been set by someone
	char *mode = "maj";	// Mode if none specified.
	int   octadj;		// Adjust note-to-staff mapping for octave.
	int   clfadj;		// Adjust note-to-staff mapping for clef.
	int   pitadj;		// Sum of octadj and clfadj
	int   root;
	int   racc;
	int   gottonic = 0;	// We've parsed a tonic note.
	int   gotmode  = 0;	// We've parsed a mode string.
	int   gotclef  = 0;	// We've parsed a clef description.
	int   gotaccs  = 0;	// We've found one or more accidentals.
	int   half  = 0;	// Tonic quarter tone accidental mark.

	V5 "%s: init=%d \"%s\" %s\n",F,init,s,dmpKsig(ks) V;
/*
* Maybe initialize with key 'none' (which is equivalent to C)
*/
	ks->data.accs = accs = sf = i = j = k = l = 0;
//	if (!ks->knum) ks->knum = ++ksignum;	// Count the Ksig structs
	if (init) {
		V5 "%s: Initialize the key signature.\n",F V;
		V6 "%s: Init new ksig=%lX to sf=0 clef=TREBLE=%d root=2 racc=0.\n",F,(ulong)ks,TREBLE V;
		ks->data.sf	 = 0;	// No sharps or flats
		ks->data.clef   = 0;	// Default clef will be treble
		ks->data.middle = 'B';	// Default middle note will be 'B'
		ks->data.octadj = 0;	// Default octave will have plain notes on staff
		ks->data.clfadj = 0;	// Staff position relative to middle=B
		ks->data.pitadj = 0;	// Total pitch adjustment
		ks->data.root   = 2;	// Default tonic is C
		ks->data.rootAc = 0;	// Rood accidentals (???)
		ks->data.accs   = 0;	// No accidentals
	}
	lastks = ks;
	clef   = ks->data.clef;
	middle = ks->data.middle;	V6 "%s: clef=%d middle='%c'\n",F,clef,middle V;
	octadj = ks->data.octadj;
	clfadj = ks->data.clfadj;
	pitadj = ks->data.pitadj;
	accs   = ks->data.accs;
	racc   = ks->data.rootAc;
	root   = ks->data.root;
	sf	 = ks->data.sf;
	V6 "%s: clef=%d middle='%c' clfadj=%d octadj=%d pitadj=%d accs=%d root=%d racc=%d sf=%d.\n",
		F,clef,middle,clfadj,octadj,pitadj,accs,root,racc,sf V;
/*
* check for "treble" "bass" "alto" with or without octave suffix
*/
	if (!nocasecmpn(s+c,"clef=", 5)) {
		gotclef = 1;	// Note we have some sort of clef
		s += 5;
		V5 "%s: Ignore \"clef=\"\n",F V;
	}
	// For treble/alto/tenor/bass, "clef=" isn't required:
	if (!nocasecmpn(s+c,"bass",  4)) {
		V2 "%s: Clef=bass \"%s\"\n",F,s+c V;
		gotclef = 1;
		clfadj = 2;
		octadj = 0;
		middle='d';
		V6 "%s: Set middle to '%c' (Line %d)\n",F,middle,__LINE__ V;
	} elsif (!nocasecmpn(s+c,"alto",  4)) {
		V2 "%s: Clef=alto \"%s\"\n",F,s+c V;
		gotclef = 1;
		clfadj = 1;
		octadj = 0;
		middle='c';
		V6 "%s: Set middle to '%c' (Line %d)\n",F,middle,__LINE__ V;
	} elsif (!nocasecmpn(s+c,"treble",6) || !nocasecmpn(s+c,"tenor",5)) {
		V2 "%s: Clef=treble/tenor \"%s\"\n",F,s+c V;
		gotclef = 1;
		clfadj = 0;
		octadj = 0;
		middle='B';
		V6 "%s: Set middle to '%c' (Line %d)\n",F,middle,__LINE__ V;
	} elsif (gotclef && !nocasecmpn(s+c,"none",4)) {
		V2 "%s: Clef=none \"%s\"\n",F,s+c V;
		clfadj = 0;
		octadj = 0;
		middle='B';
		V6 "%s: Set middle to '%c' (Line %d)\n",F,middle,__LINE__ V;
	}
//	if (!strcasecmp(s,"treble"))   {clef = TREBLE;   clfadj = 0; octadj = 0; gotclef = 1;}
//	if (!strcasecmp(s,"treble+8")) {clef = TREBLEu8; clfadj = 0; octadj = 0; gotclef = 1;}
//	if (!strcasecmp(s,"treble-8")) {clef = TREBLEd8; clfadj = 0; octadj = 0; gotclef = 1;}
//	if (!strcasecmp(s,"bass"))	 {clef = BASS;	 clfadj = 2; octadj = 0; gotclef = 1;}
//	if (!strcasecmp(s,"bass-8"))   {clef = BASSd8;   clfadj = 2; octadj = 0; gotclef = 1;}
//	if (!strcasecmp(s,"bass+8"))   {clef = BASSu8;   clfadj = 2; octadj = 0; gotclef = 1;}
//	if (!strcasecmp(s,"alto"))	 {clef = ALTO;	 clfadj = 1; octadj = 0; gotclef = 1;}
//	if (!strcasecmp(s,"alto-8"))   {clef = ALTOd8;   clfadj = 1; octadj = 0; gotclef = 1;}
//	if (!strcasecmp(s,"alto+8"))   {clef = ALTOu8;   clfadj = 1; octadj = 0; gotclef = 1;}
/*
* If we got a clef without a key signature, this is just a clef change.
* We must leave the key signature alone.
*/
	if (gotclef) {
		V6 "%s: Got clef %s, reuse keysig %s\n",F,s,dmpKsig(ks) V;
		V6 "%s: clef=%d middle='%c'\n",F,clef,middle V;
		gottonic = gotmode  = 1;
		ks->data.clef   = clef;
		ks->data.clfadj = clfadj;
		ks->data.octadj = octadj;
		ks->data.pitadj = clfadj + octadj;
		V6 "%s: Got clef %s, reuse keysig %s\n",F,s,dmpKsig(ks) V;
	} else {
		V4 "%s: Parse the keysig string \"%s\"\n",F,s V;
		bagpipe = 0;
	/*
	* Check for a tonic, which should be a note letter or 'H' for highland pipe keys.
	*/
		if (gottonic) {
			V6 "%s: Tonic %c%c c=%d sf=%d root=%d racc=%d already determined.\n",F,tonic,tacc,c,sf,root,racc V;
		} else {
			V6 "%s: Get tonic from \"%s\"\n",F,s+c V;
			switch (tonic = s[c]) {
			case 'F': case 'f': sf = -1; root = 5; ++c; gottonic++; break;
			case 'C': case 'c': sf =  0; root = 2; ++c; gottonic++; break;
			case 'G': case 'g': sf =  1; root = 6; ++c; gottonic++; break;
			case 'D': case 'd': sf =  2; root = 3; ++c; gottonic++; break;
			case 'A': case 'a': sf =  3; root = 0; ++c; gottonic++; break;
			case 'E': case 'e': sf =  4; root = 4; ++c; gottonic++; break;
			case 'B': case 'b': sf =  5; root = 1; ++c; gottonic++; break;
			case 'H': case 'h':
				bagpipe = 1;
				c++;
				if	(s[c] == 'P') { sf = 0; root = 2; }
				elsif (s[c] == 'p') { sf = 2; root = 3; }
				else wng("unknown bagpipe-like key: ",s);
				gottonic++;
				break;
			default:
				V3 "%s: Key not recognised: \"%s\"\n",F,s V;
				sf = 0; root = 2;	/* Default to C */
			}
			V3 "%s: Tonic %c%c at c=%d has sf=%d root=%d.\n",F,tonic,tacc,c,sf,root V;
		/*
		* Check for an accidental after the tonic, either # or b.
		*/
		//	racc = A_NT;
			/*Check for quarter tone*/
			half = 0;
			if (s[c] == '/') {
					half = 1;
				c += 1;
			}
			if (s[c] == '#') {
				tacc = '#';
				sf += 7;
				c += 1;
				racc = (half ? A_HSH : A_SH);
				V6 "%s: racc=A_%sSH=%d\n",F,(half?"S":""),racc V;
			} elsif (s[c] == 'b') {
				tacc = 'b';
				sf -= 7;
				c += 1;
				racc = (half ? A_HFT : A_FT);
				V6 "%s: racc=A_%sFT=%d\n",F,(half?"S":""),racc V;
			}
			V6 "%s: Tonic %c%c at c=%d has sf=%d root=%d racc=%d.\n",F,tonic,tacc,c,sf,root,racc V;
		}
	}
	V6 "%s: Keysig %s\n",F,dmpKsig(ks) V;
	if (gottonic && sf) {
		V6 "%s: Got tonic %c%c with sf=%d.\n",F,'A'+root,tacc,sf V;
//		ks->data.accs = 0;
//		ks->data.accs = accs = sf = 0;
	}
//	if (sf != 0) {
//		V3 "%s: Add %d accidentals to key signature.\n",F,sf V;
//		addMode(ks,sf);
//		ks->data.root = root;
//		ks->data.rootAc = racc;
//	}
	V6 "%s: Keysig %s\n",F,dmpKsig(ks) V;
/*
* Loop over blank-delimited words: get the next token.  The first token after
* the  tonic  may  be a mode or a list of accidentals.  Note that we fill w[]
* with the word, but we can still find  the  origin at s[c-j].
*/
	V6 "%s: parse \"%s\" ...\n",F,s+c V;
	for (;;) {
		V6 "%s: Parse \"%s\"\n",F,s+c V;
		while (isspace(s[c])) c++;	// Skip over spaces
		if (s[c] == '%') break;		// Stop at comment
		if (s[c] == '\0') break;	// Stop at end of line
		V5 "%s: Next: \"%s\"\n",F,s+c V;
		j = 0;						// Offset to current char in w[]
		while (!(isspace(s[c]) || (s[c]=='\0'))) {
			w[j++] = s[c++];		// Copy to w[]
		}
		w[j] = '\0';				// w[] is the next word
		V6 "%s: Word: \"%s\"\n",F,w V;
		if (!gotmode) {
			V6 "%s: Check for mode.\n",F V;
			if (w[0] == '*') {		// Kludge to fake a missing mode
				V6 "%s: Explicit key signature \"%s\"\n",F,w V;
				sf = 0;
			//	mode = "";
			//	ks->data.accs = accs = sf = 0;
				eksp = w;			// Note start of check for explicit keysig
				continue;
			} elsif ((w[0] == '^') || (w[0] == '=') || (w[0] == '_')) {
				V6 "%s: Explicit key signature \"%s\"\n",F,w V;
				sf = 0;
			//	mode = "";
			//	ks->data.accs = accs = sf = 0;
				eksp = w;			// Start of explicit keysig
				V6 "%s: Explicit key signature \"%s\"\n",F,eksp V;
			} elsif (!nocasecmpn(w,"exp",3) || !nocasecmpn(w,"oct",3)) {
				V6 "%s: Explicit key signature \"%s\"\n",F,w V;
				sf = 0;
				eksp = w+3;			// Start of explicit keysig
				V6 "%s: Explicit key signature \"%s\"\n",F,w V;
			} elsif ((nocasecmpn(w,"mix",3)) == 0) {
				sf -= 1;
				ok = gotmode = 1;
				eksp = w+3;
			/* dorian mode on the second note (D in C scale) */
			} elsif ((nocasecmpn(w,"dor",3)) == 0) {
				sf -= 2;
				ok = gotmode = 1;
				eksp = w+3;
			/* phrygian mode on the third note (E in C scale) */
			} elsif ((nocasecmpn(w,"phr",3)) == 0) {
				sf -= 4;
				ok = gotmode = 1;
				eksp = w+3;
			/* lydian mode on the fourth note (F in C scale) */
			} elsif ((nocasecmpn(w,"lyd",3)) == 0) {
				sf += 1;
				ok = gotmode = 1;
				eksp = w+3;
			/* locrian mode on the seventh note (B in C scale) */
			} elsif ((nocasecmpn(w,"loc",3)) == 0) {
				sf -= 5;
				ok = gotmode = 1;
				eksp = w+3;
			/* major and ionian are the same ks */
			} elsif ((nocasecmpn(w,"maj",3)) == 0) {
				ok = gotmode = 1;
				eksp = w+3;
			} elsif ((nocasecmpn(w,"ion",3)) == 0) {
				ok = gotmode = 1;
				eksp = w+3;
			/* aeolian, m, minor are the same ks - sixth note (A in C scale) */
			} elsif ((nocasecmpn(w,"aeo",3)) == 0) {
				sf -= 3;
				ok = gotmode = 1;
				eksp = w+3;
			} elsif ((nocasecmpn(w,"min",3)) == 0) {
				sf -= 3;
				ok = gotmode = 1;
				eksp = w+3;
			} elsif ((nocasecmpn(w,"m",1)) == 0) {
				sf -= 3;
				ok = gotmode = 1;
				eksp = w+1;
			}
		}
		V5 "%s: Check \"%s\" for \"middle=\" (middle='%c')\n",F,w,middle V;
		if (!(i = nocasecmpn(w,"m=",k=2)) || !(i = nocasecmpn(w,"middle=",k=7))) {
			V5 "%s: Recognized 'middle='\n",F V;
			++gotmiddle;
			if ((c1 = w[k]) && (within('A',c1,'G') || within('a',c1,'g'))) {
				V5 "%s: Found note '%c' middle was '%c'\n",F,c1,middle V;
				++k;
				clfadj = middleadj(middle = c1);
				V5 "%s: octadj=%d clfadj=%d pitadj=%d after '%c' note.\n",F,octadj,clfadj,pitadj,c1 V;
				while ((c2 = w[k]) && ((c2 == ',') || ( c2 == '\''))) {
					if (c2 ==  ',') octadj += 7;
					if (c2 == '\'') octadj -= 7;
					V5 "%s: octadj=%d clfadj=%d pitadj=%d after '%c' octave.\n",F,octadj,clfadj,pitadj,c2 V;
					++k;
				}
			} else {
				V1 "%s: Unrecognized note '%c' in \"%s\"\n",F,c1,w V;
			}
			continue;
		}
		V5 "%s: Check \"%s\" for clef= ...\n",F,w V;
		k = 0;			// Offset into w[] for tests
		if (!nocasecmpn(w,"clef=",5)) {
			gotclef = 1;
			k = 5;
			octadj = clfadj = pitadj = 0;
			V5 "%s: Ignore %d chars of \"%s\"\n",F,k,w V;
			V6 "%s: Check \"%s\" for clef name (middle='%c')\n",F,w+k,middle V;
			if	  (!nocasecmpn(w+k,"bass",4))   {k += 4; middle = 'd'; octadj = 0; clef = BASS;	clfadj = 2;
			} elsif (!nocasecmpn(w+k,"alto",4))   {k += 4; middle = 'c'; octadj = 0; clef = ALTO;	clfadj = 1;
			} elsif (!nocasecmpn(w+k,"treble",6)) {k += 6; middle = 'B'; octadj = 0; clef = TREBLE;
			} elsif (!nocasecmpn(w+k,"tenor",6))  {k += 6; middle = 'B'; octadj = 0; clef = TENOR;
			} elsif (!nocasecmpn(w+k,"F",1))	  {k += 1; middle = 'd'; octadj = 0; clef = BASS;	clfadj = 2;
			} elsif (!nocasecmpn(w+k,"G",1))	  {k += 1; middle = 'B'; octadj = 0; clef = TREBLE;
			} elsif (!nocasecmpn(w+k,"C",1))	  {k += 1; middle = 'c'; octadj = 0; clef = ALTO;	clfadj = 1;
			} elsif (!nocasecmpn(w+k,"none",4))   {
				if (gotclef) {k += 4; middle = 'B'; octadj = clfadj = 0; clef = NOCLEF;}
			} else {
				V5 "%s: No clef in key specifier: \"%s\"\n",F,w+k V;
			}
			V5 "%s: k=%d clef=%d middle='%c' clfadj=%d octadj=%d after clef check.\n",F,k,clef,middle,clfadj,octadj V;
		}
		/* check for "+8" or "-8" */
		if	  (!nocasecmpn(w+k,"-8",2)) {k += 2; clef += 1;	// clfadj = -7;
		} elsif (!nocasecmpn(w+k,"+8",2)) {k += 2; clef += 2;	// clfadj =  7;
		} else {
			V6 "%s: No octave in key specifier: \"%s\"\n",F,w+k V;
		}
		V5 "%s: Got k=%d root=%d sf=%d clef=%d clfadj=%d octadj=%d pitadj=%d.\n",
			F,k,root,sf,clef,clfadj,octadj,pitadj V;
		ks->data.clef = clef;
		if (sf) {
			accs = fillKsig(ks,sf);
			V6 "%s: Ksig=%lX has sf=%d accs=%d.\n",F,(ulong)ks,sf,ks->data.accs V;
		}
		if (eksp) {
			V6 "%s: Keysig check at \"%s\"\n",F,eksp V;
			if ((a = parseKsig(ks,eksp))) {
				gotaccs += a;
				V6 "%s: Keysig now has %d accidentals.\n",F,a V;
				if (a > accs) {
					V6 "%s: Keysig changed from %d to %d accidentals.\n",F,accs,a V;
					sf = a;	/* Explicit list always has positive count */
				} else {
					V6 "%s: Keysig left at sf=%d.\n",F,sf V;
				}
			} else {
				V6 "%s: No explicit keysig.\n",F V;
			}
		}
	}  /* end of loop over blank-delimted words */
	V3 "%s: Done with parse; gottonic=%d gotmode=%d gotaccs=%d sf=%d accs=%d clfadj=%d octadj=%d.\n",
		F,gottonic,gotmode,gotaccs,sf,accs,clfadj,octadj V;
	if (!gotmiddle) {
		V6 "%s: Middle note '%c' not parsed.\n",F,middle V;
		if ((middle && within('A',middle,'G')) || within('a',middle,'g')) {
			V5 "%s: Found middle note '%c'\n",F,middle V;
			++k;
			clfadj = middleadj(middle);
			V5 "%s: octadj=%d clfadj=%d after middle '%c' note.\n",F,octadj,clfadj,middle V;
		} else {
			V1 "%s: Unrecognized middle note '%c' in \"%s\"\n",F,middle,w V;
		}
	}
	if (!clfadj) {
		V6 "%s: Get clef offset clfadj for middle='%c'...\n",F,middle V;
		clfadj = middleadj(middle);
	}
	V3 "%s: Got clef offset clfadj=%d for middle='%c'\n",F,clfadj,middle V;
	if (!gotmode && !gotaccs) {
		V3 "%s: Default key is %c%c major.\n",F,tonic,tacc V;
		ks->data.accs = 0;
		if (sf != 0) {
			V3 "%s: Add %d accidentals to key signature.\n",F,sf V;
			addMode(ks,sf);
		}
	}
/* copy to struct */
	ks->data.sf	 = 0;
	ks->data.clef   = clef;
	ks->data.middle = middle;
	ks->data.octadj = octadj;
	ks->data.clfadj = clfadj;
	ks->data.pitadj = clfadj + octadj;	// Total "pitch" adjustment to position notes correctly
	ks->data.root   = root;
	ks->data.rootAc = racc;
	V6 "%s: Ksig has sf=%d, %d accidentals, root=%d rootAc=%d clfadj=%d octadj=%d pitadj=%d.\n",
		F,ks->data.sf,ks->data.accs,ks->data.root,ks->data.rootAc,
		ks->data.clfadj,ks->data.octadj,ks->data.pitadj V;
	for (i = 1; i<ks->data.accs; i++) {
		if (ks->data.atyp[i] != ks->data.atyp[i-1]) {
			ks->data.clef = A_MX;
			V3 "%s: This keysig has mixed accidentals.\n",F V;
			break;
		}
	}
	V6 "%s: %s done, return 1.\n",F,dmpKsig(ks) V;
	return 1;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Calculate the adjustment for a specified "middle=" note.
* This is in "pitch" units.
*/
int middleadj(char note)
{	char *F = "middleadj";
	int   off = 0;
	switch (note) {
	case 'C': off =  6; break;
	case 'D': off =  5; break;
	case 'E': off =  4; break;
	case 'F': off =  3; break;
	case 'G': off =  2; break;
	case 'A': off =  1; break;
	case 'B': off =  0; break;	// Default treble note-staff map.
	case 'c': off = -1; break;
	case 'd': off = -2; break;
	case 'e': off = -3; break;
	case 'f': off = -4; break;
	case 'g': off = -5; break;
	case 'a': off = -6; break;
	case 'b': off = -7; break;
	default:
		V1 "%s: Char '%c' is not a note.\n",F,note V;
	}
	V6 "%s: Note '%c' is %d.\n",F,note,off V;
	return off;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
int addMode(
	Ksig *ks,	// Ksig struct
	int   sf,	// Sharps or flats
	int   cl)	// Clef type
{	char *F = "addMode";
	int   a, n;
	n = ks->data.accs;
	V6 "%s: Add %d accidentals to %d in key signature.\n",F,sf,n V;
	if (sf < 0) {
		V6 "%s: Add %d flats.\n",F,sf V;
		for (a=0; a<-sf; a++) {
			ks->data.atyp[n] = A_FT;
			ks->data.aval[n] = fl_val[a];
			ks->data.apos[n] = note_pos(fl_val[a],cl);
			V6 "%s: Add flat %d val='%c' pos=%d.\n",F,n,ks->data.aval[n],ks->data.apos[n] V;
			n++;
		}
	} elsif (sf > 0) {
		V6 "%s: Add %d sharps.\n",F,sf V;
		for (a=0; a<sf; a++) {
			ks->data.atyp[n] = A_SH;
			ks->data.aval[n] = sh_val[a];
			ks->data.apos[n] = note_pos(sh_val[a],cl);
			V6 "%s: Add sharp %d val='%c' pos=%d.\n",F,n,ks->data.aval[n],ks->data.apos[n] V;
			n++;
		}
	} else {
		V6 "%s: No accidentals to add (sf=%d)\n",F,sf V;
	}
	V6 "%s: There are now %d accidentals in the key signature.\n",F,n V;
	ks->data.accs = n;
	return n;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This calculates the note position on the staff from the note name. The scale
* used has the bottom line at 0 and 6 units per line.
*/
int note_pos(
	char  note,
	int   clef)
{	char *F = "note_pos";
	int   offset = 0;
	int   val;
	V6 "%s: offset=%d.\n",F,offset V;
	switch (note) {
	case 'E': val =  0; break;
	case 'F': val =  3; break;
	case 'G': val =  6; break;
	case 'A': val =  9; break;
	case 'B': val = 12; break;
	case 'c': val = 15; break;
	case 'd': val = 18; break;
	case 'e': val = 21; break;
	case 'f': val = 24; break;
	case 'g': val = 27; break;
	default:  val = -3; break;
	}
	V6 "%s: offset=%d val=%d return %d.\n",F,offset,val,val+offset V;
	return (val+offset);
}

/* ------- parseKsig: Parse explicit key signatures and add to keysig -------
* This handles an explicit keysig (a list of  notes  with  accidentals),  and
* fills in ks->data with the results.  Added by jc 2000-06.
*/
int parseKsig(
	Ksig *ks,		// Key signature struct
	char *s,		// Original copy of current word, not terminated
	int   clef)		// Clef type
{	char *F = "parseKsig";
	char  c, d,n;			/* Chars being examined */
	int   a = ks->data.accs;	/* Accidental counter */
	int   half;
	V6 "%s: Called for s=\"%s\"\n",F,s V;
	while ((c = *s) && ((c == '*') || isspace(c) || isalpha(c))) {
		V6 "%s: Skip '%c'\n",F,c V;
		++s;
	}
	if (!c) {
		V3 "%s: No extra stuff on keysig.\n",F V;
		return a;
	}
	V6 "%s: Keysig \"%s\"\n",F,s V;
	c = d = 0;
	while ((c = *s)) {
		V6 "%s: Parse \"%s\"\n",F,s V;
		while ((c = *s) && isspace(c)) ++s;
		V6 "%s: '%c' ...\n",F,c V;
		switch (c) {
		case '^':
		case '=':
		case '_':
				if ((half = (s[1] == '/'))) s++; /*look for half sharp/flat*/
			if ((d = s[1]) && (('A' <= d && d <= 'G') || ('a' <= d && d <= 'g'))) {
				V6 "%s: Accidental '%c' note '%c'.\n",F,c,d V;
				ks->data.atyp[a] = (c == '^') ? (half ? A_HSH : A_SH) : (c == '_' ) ? (half ? A_HFT : A_FT) : A_NT;
				ks->data.aval[a] = d;
				ks->data.apos[a] = note_pos(d,clef);
				V6 "%s: ks[%d] '%c%c' atyp=%d apos=%d.\n",F,
					a,c,ks->data.aval[a],ks->data.atyp[a],ks->data.apos[a] V;
				a++;
				s += 2;
			} elsif (d) {
				V1 "%s: %02X='%c' followed by invalid char %02X='%c'\n",F,c,c,d,d V;
				++s;
			} else {
				V1 "%s: %02X='%c' followed by funny char '%02X'\n",F,c,c,d V;
				++s;
			}
			continue;
		default:
			V4 "%s: %02X='%c' ends tonic+mode portion of keysig.\n",F,c,c V;
			c = d = 0;
			break;
		}
		if (c == 0) break;
	}
	if (c) {
		V6 "%s: '%c' terminates explicit keysig at \"%s\".\n",F,c,s V;
	}
	ks->data.accs = a;
	V3 "%s: There are %d accidentals in this key signature; \"%s\" left.\n",F,ks->data.accs,s V;
	if ((n = c = *s) && (d = tolower(c)) && ('a' <= d) && (d <= 'g')) {
		V3 "%s: Tonic note '%c'.\n",F,c V;
		ks->data.root = (d - 'a');
		if ((c = *++s) && (d = tolower(c)) && (d == 'b' || d == '#')) {
			V3 "%s: Tonic note n='%c' d='%c' .\n",F,n,d V;
			ks->data.rootAc = (d == '#') ? A_SH : A_FT;
			if(s[1] == '/') {
			  ks->data.rootAc = (d == '#') ? A_HSH : A_HFT;
			  ++s;
			}
			++s;
		}
	} elsif (*s) {
		V3 "%s: Can't parse remainder of keysig \"%s\"\n",F,s V;
	}
	while ((c = *s) && isspace(c)) ++s;	/* Skip spaces looking for mode */
	if ((c = *s)) {
		if (isalpha(c)) {
			strncpy(ks->data.mode,s,15);
			V3 "%s: Mode \"%s\"\n",F,ks->data.mode V;
		} elsif (*s) {
			V3 "%s: No mode in remainder of keysig \"%s\"\n",F,s V;
		}
	}
	if ((c = *s)) {
		V3 "%s: Tail of keysig \"%s\" unused.\n",F,s V;
	}
	return a;
}

/* ----- get_halftones: figure out how by many halftones to transpose --- */
/*  In the transposing routines: pitches A..G are coded as with 0..7 */
int get_halftones (key, transpose)
	Ksig key;
	char transpose[];
{
	int pit_old,pit_new,direction,stype,root_new,racc_new,nht;
	int root_old, racc_old;
	char *q;
	/* pit_tab associates true pitches 0-11 with letters A-G */
	int pit_tab[] = {0,2,3,5,7,8,10};

	if (strlen(transpose) == 0) return 0;
	root_old = key.data.root;
	racc_old = key.data.rootAc;

	/* parse specification for target key */
	q = transpose;
	direction = 0;

	if (*q == '^') {
		direction = 1; q++;
	} elsif (*q == '_') {
		direction = -1; q++;
	}
	stype = 1;
	if (strchr("ABCDEFG",*q)) {
		root_new = *q-'A'; q++; stype = 2;
	} elsif (strchr("abcdefg",*q)) {
		root_new = *q-'a'; q++;  stype = 2;
	}

	/* first case: offset was given directly as numeric argument */
	if (stype == 1) {
		sscanf(q,"%d", &nht);
		if (direction<0) nht = -nht;
		if (nht == 0) {
			if (direction<0) nht = -12;
			if (direction>0) nht = +12;
		}
		return nht;
	}

	/* second case: root of target key was specified explicitly */
	racc_new = 0;
	if (*q == 'b') {
		racc_new = A_FT; q++;
		if(*q == '/') { racc_new = A_HFT; q++;}
	} elsif (*q == '#') {
		racc_new = A_SH; q++;
		if(*q == '/') { racc_new = A_HSH; q++;}
	} elsif (*q != '\0')
		wng ("expecting accidental in transpose spec: ", transpose);

	/* get pitch as number from 0-11 for root of old key */
	pit_old = pit_tab[root_old];
	if (racc_old == A_FT) pit_old--;
	if (racc_old == A_SH) pit_old++;
	/*todo:
	  if (racc_old == A_HFT) pit_old -= 0.5;
	  if (racc_old == A_HSH) pit_old += 0.5;
	*/
	if (pit_old<0)  pit_old += 12;
	if (pit_old>11) pit_old -= 12;

	/* get pitch as number from 0-11 for root of new key */
	pit_new = pit_tab[root_new];
	if (racc_new == A_FT) pit_new--;
	if (racc_new == A_SH) pit_new++;
	/*todo:
	  if (racc_new == A_HFT) pit_new-=0.5;
	  if (racc_new == A_HSH) pit_new+=0.5;
	*/
	if (pit_new<0)  pit_new += 12;
	if (pit_new>11) pit_new -= 12;

	/* number of halftones is difference */
	nht = pit_new-pit_old;
	if (direction == 0) {
		if (nht>6)  nht -= 12;
		if (nht<-5) nht += 12;
	}
	if (direction>0 && nht <= 0) nht += 12;
	if (direction<0 && nht >= 0) nht -= 12;

	return nht;

}

/* ----- shift_key: make new key by shifting nht halftones --- */
void shift_key (sf_old,nht,sfnew,addt)
int sf_old,nht,*sfnew,*addt;
{
	int sf_new,r_old,r_new,add_t,  dh,dr;
	int skey_tab[] = {2,6,3,0,4,1,5,2};
	int fkey_tab[] = {2,5,1,4,0,3,6,2};
	char root_tab[] = {'A','B','C','D','E','F','G'};

	/* get sf_new by adding 7 for each halftone, then reduce mod 12 */
	sf_new = sf_old+nht*7;
	sf_new = (sf_new+240)%12;
	if (sf_new >= 6) sf_new = sf_new-12;

	/* get old and new root in ionian mode, shift is difference */
	r_old = 2;
	if (sf_old>0) r_old = skey_tab[sf_old];
	if (sf_old<0) r_old = fkey_tab[-sf_old];
	r_new = 2;
	if (sf_new>0) r_new = skey_tab[sf_new];
	if (sf_new<0) r_new = fkey_tab[-sf_new];
	add_t = r_new-r_old;

	/* fix up add_t to get same "decade" as nht */
	dh = (nht+120)/12; dh = dh-10;
	dr = (add_t+70)/7; dr = dr-10;
	add_t = add_t+7*(dh-dr);

	V3 "shift_key: sf_old = %d new %d   root: old %c new %c  shift by %d\n", sf_old, sf_new, root_tab[r_old], root_tab[r_new], add_t V;

	*sfnew = sf_new;
	*addt = add_t;

}

/*
* tables for pretty printout only
*/
char root_tab[] = {'A','B','C','D','E','F','G'};
char acc_tab[][3] ={"bb","b ","  ","# ","x "};

/* ----- set_transtab: setup for transposition by nht halftones --- */
void set_transtab (nht,key)
	int nht;
	Ksig *key;
{	char *F = "set_transtab";
	int a,b,sf_old,sf_new,add_t,i,j,acc_old,acc_new,root_old,rootAc;
	char c1[6],c2[6],c3[6];

	/* nop if no transposition is wanted */
	if (nht == 0) {
		key->data.add_transp = 0;
		for (i = 0;i<7;i++) key->data.add_acc[i] = 0;
		return;
	}

	/* get new sharps_flats and shift of numeric pitch; copy to key */
	sf_old   = key->data.sf;
	root_old = key->data.root;
	rootAc = key->data.rootAc;
	shift_key (sf_old, nht, &sf_new, &add_t);
	key->data.sf = sf_new;
	key->data.add_transp = add_t;

	/* set up table for conversion of accidentals */
	for (i = 0;i<7;i++) {
		j = i+add_t;
		j = (j+70)%7;
		acc_old = 0;
		if ( sf_old >= sh_tab[i]) acc_old =  1;
		if (-sf_old >= fl_tab[i]) acc_old = -1;
		acc_new = 0;
		if ( sf_new >= sh_tab[j]) acc_new =  1;
		if (-sf_new >= fl_tab[j]) acc_new = -1;
		key->data.add_acc[i] = acc_new-acc_old;
	}

	/* printout keysig change */
	if (verbose >= 3) {
		i = root_old;
		j = i+add_t;
		j = (j+70)%7;
		acc_old = 0;
		if ( sf_old >= sh_tab[i]) acc_old = 1;
		if (-sf_old >= fl_tab[i]) acc_old = -1;
		acc_new = 0;
		if ( sf_new >= sh_tab[j]) acc_new = 1;
		if (-sf_new >= fl_tab[j]) acc_new = -1;
		strcpy(c3,"s"); if (nht == 1 || nht == -1) strcpy(c3,"");
		strcpy(c1,""); strcpy(c2,"");
		if (acc_old == -1) strcpy(c1,"b"); if (acc_old == 1) strcpy(c1,"#");
		if (acc_new == -1) strcpy(c2,"b"); if (acc_new == 1) strcpy(c2,"#");
		fprintf(stderr,"Transpose root from %c%s to %c%s (shift by %d halftone%s)\n",
						root_tab[i],c1,root_tab[j],c2,nht,c3);
	}

	/* printout full table of transformations */
	if (verbose >= 4) {
		fprintf(stderr,"old & new keysig	conversions\n");
		for (i = 0;i<7;i++) {
			j = i+add_t;
			j = (j+70)%7;
			acc_old = 0;
			if ( sf_old >= sh_tab[i]) acc_old = 1;
			if (-sf_old >= fl_tab[i]) acc_old = -1;
			acc_new = 0;
			if ( sf_new >= sh_tab[j]) acc_new = 1;
			if (-sf_new >= fl_tab[j]) acc_new = -1;
			fprintf(stderr,"%c%s-> %c%s		   ", root_tab[i],acc_tab[acc_old+2],
						 root_tab[j],acc_tab[acc_new+2]);
			for (a = -1;a <= 1;a++) {
				b = a+key->data.add_acc[i];
				fprintf(stderr,"%c%s-> %c%s  ",root_tab[i],acc_tab[a+2],root_tab[j],acc_tab[b+2]);
			}
			fprintf(stderr,"\n");
		}
	}
}

/* ----- do_transpose: transpose numeric pitch and accidental --- */
void do_transpose (
	Ksig *key,
	int  *pitch,
	int  *acc)
{	char *F="do_transpose";
	int pitch_old,pitch_new,sf_old,sf_new,acc_old,acc_new,i,j;

	V6 "%s: Ksig %s\n",F,dmpKsig(key) V;
	pitch_old = *pitch;
	acc_old   = *acc;
	pitch_new = pitch_old + key->data.pitadj;	// No tranpose for now [jc]  + key->data.add_transp;
	i = (pitch_old+70)%7;
	j = (pitch_new+70)%7;
	V6 "%s: Pitch old=%d new=%d.\n",F,pitch_old,pitch_new V;

	if (acc_old) {
		if (acc_old == A_DF) sf_old = -2;
		/*todo: if (acc_old == A_HFT) sf_old = -0.5;*/
		if (acc_old == A_FT) sf_old = -1;
		if (acc_old == A_NT) sf_old = 0 ;
		/*todo: if (acc_old == A_HSH) sf_old = 0.5; */
		if (acc_old == A_SH) sf_old = 1;
		if (acc_old == A_DS) sf_old = 2;
		sf_new = sf_old+key->data.add_acc[i];
		if (sf_new == -2) acc_new = A_DF;
		if (sf_new == -1) acc_new = A_FT;
		/*todo: if (sf_new == -0.5) acc_new = A_HFT; */
		if (sf_new == 0) acc_new = A_NT;
		/*todo: if (sf_new == 0.5) acc_new = A_HSH;*/
		if (sf_new == 2) acc_new = A_DS;
	} else {
		acc_new = 0;
	}
	*pitch = pitch_new;
	*acc   = acc_new;
}

/* ----- ach_transpose: transpose accomp chord string in ach --- */
void ach_transpose (
	Ksig *key)
{
	char *q,*r;
	char str[201];
	int root_old,root_new,sf_old,sf_new,ok;
	char root_tab[] = {'A','B','C','D','E','F','G'};
	char root_tub[] = {'a','b','c','d','e','f','g'};

	if (halftones == 0) return;

	/* try to avoid some common abuses of achord string */
	if (strstr(ach,"capo")) return;
	if (strstr(ach,"Capo")) return;
	if (strstr(ach,"Fine")) return;
	if (strstr(ach,"fine")) return;

	q = ach;
	r = str;

	for (;;) {
		while (*q == ' ' || *q == '(') { *r = *q; q++; r++; }
		if (*q == '\0') break;
		ok = 0;
		if (strchr("ABCDEFG",*q)) {
			root_old = *q-'A'; q++; ok = 1;
		} elsif (strchr("abcdefg",*q)) {
			root_old = *q-'a'; q++; ok = 2;
		}

		if (ok) {
			sf_old = 0;
			if (*q == 'b') { sf_old = -1; q++; }
			if (*q == '#') { sf_old= 1; q++; }
			root_new = root_old + key->data.add_transp;
			root_new = (root_new+28)%7;
			sf_new = sf_old + key->data.add_acc[root_old];
			if (ok == 1) { *r = root_tab[root_new]; r++; }
			if (ok == 2) { *r = root_tub[root_new]; r++; }
			if (sf_new == -1) { *r = 'b'; r++; }
			if (sf_new == 1) { *r = '#'; r++; }
		}

		while (*q != ' ' && *q != '/' && *q != '\0') {*r = *q; q++; r++; }
		if (*q == '/') {*r = *q; q++; r++; }

	}

	*r = '\0';
/*|   fprintf(stderr,"tr_ch: <%s>  <%s>\n", ach, str);   |*/

	strcpy (ach,str);

}


/* ----- init_parse_params: initialize variables for parsing --- */
void init_parse_params ()
{	char *F = "init_parse_params";
	int i;

	slur = 0;
	voice[0].end_slur = 0;
	voice[0].rem_slur = 0;	// Added by jc to stop remembering slurs from previous tune
	nwpool = nwline = 0;
	ntinext = 0;

	/* for continuation after output: reset nsym, switch to first voice */
	for (i = 0;i<nvoice;i++) {
		voice[i].nsym = 0;
		voice[i].insert_btype = 0;
		voice[i].end_slur = 0;
	}
	ivc = 0;
	word = 0;
	carryover = 0;
	last_note = last_real_note = -1;
	pplet = qplet = rplet = 0;
	num_ending = 0;
	mes1 = mes2 = 0;
	strcpy(ach, "");
}

/* ----- add_text ---- */
void add_text (str,type)
	char str[];
	int type;
{	char *F = "add_text";
	if (! make_ps) return;
	if (ntext >= NTEXT) {
		wng ("No more room for text line <%s>", str);
		return;
	}
	strcpy (text[ntext], str);
	text_type[ntext] = type;
	ntext++;
}

/* ----- reset_info ---- */
void reset_info (inf)
	Info *inf;
{	char *F = "reset_info";
	/* reset all info fields except info.xref */

	strcpy(inf->parts,  "");
	strcpy(inf->area,   "");
	inf->nbook = 0;
	inf->ncomp = 0;
	strcpy(inf->disc,   "");
	strcpy(inf->group,  "");
	strcpy(inf->hist,   "");
	strcpy(inf->info,   "");
	strcpy(inf->key,	"C");
	strcpy(inf->meter,  "4/4");
	strcpy(inf->notes,  "");
	strcpy(inf->orig,   ""); V2 "%s: Set inf->orig to \"%s\"\n",F,inf->orig V;
	strcpy(inf->rhyth,  "");
	strcpy(inf->src,	"");
/*  strcpy(inf->title,  "(untitled)");  */
	strcpy(inf->title,  "");
	strcpy(inf->title2, "");
	strcpy(inf->title3, "");
	strcpy(inf->title4, "");
	strcpy(inf->title5, "");
	strcpy(inf->title6, "");
	strcpy(inf->trans,  "");
	strcpy(inf->tempo,  "");
}

/* ----- get_default_info: set info to default, except xref field --- */
void get_default_info ()
{
	char savestr[STRL];

	strcpy (savestr, info.xref);
	info = default_info;
	strcpy (info.xref, savestr);

}

/* ----- is_info_field: identify any type of info field ---- */
int is_info_field(str)
char str[];
{
	if (strlen(str)<2) return 0;
	if (str[1] != ':')   return 0;
	if (str[0] == '|')   return 0;   /* |: at start of music line */
	return 1;
}

/* ----- is_end_line: identify eof ----- */
int is_end_line (str)
char str[];
{
	if (strlen(str)<3) return 0;
	if (str[0] == 'E' && str[1] == 'N' && str[2] == 'D') return 1;
	return 0;
}

/* ----- is_pseudocomment ----- */
int is_pseudocomment (str)
char str[];
{
	if (strlen(str)<2) return 0;
	if ((str[0] == '%')&&(str[1] == '%'))  return 1;
	return 0;
}

/* ----- is_comment ----- */
int is_comment (str)
char str[];
{
	if (strlen(str)<1) return 0;
	if (str[0] == '%')  return 1;
	if (str[0] == '\\') return 1;
	return 0;
}


/* ----- trim_title: move trailing "The" to front ------------ */
void trim_title (s,s0)
char s[],s0[];
{
	char *q;
	char rest[81],str[301];
	int done;

	strcpy (str, s0);
	done = 0;

	if ((q = strchr(str,','))) {
		if (*q != '\0') {
			strip(rest,q+1);
			if (!strcmp(rest,"The")) {
				strcpy (s, rest);
				*q = '\0';
				strcat (s, " ");
				strcat (s, str);
				done = 1;
			}
		}
	}

	if (!done) strcpy (s,s0);

}

Ksig* asgnKsig(
	Ksig *dstkey,
	Ksig *srckey)
{
	return copyKsig(srckey,dstkey);
}

Ksig* copyKsig(
	Ksig *srckey,
	Ksig *dstkey)
{	char *F = "copyKsig";
	int   a;
	if (!dstkey) {dstkey = newKsig(F);}
//	if (!srckey->knum) srckey->knum = ++ksignum;
//	if (!dstkey->knum) dstkey->knum = ++ksignum;
	dstkey->data.clef   = srckey->data.clef;
	dstkey->data.sf	 = srckey->data.sf;
	dstkey->data.middle = srckey->data.middle;
	dstkey->data.octadj = srckey->data.octadj;
	dstkey->data.clfadj = srckey->data.clfadj;
	dstkey->data.pitadj = srckey->data.pitadj;
	dstkey->data.root   = srckey->data.root;
	dstkey->data.rootAc = srckey->data.rootAc;
	dstkey->data.accs   = srckey->data.accs;
	for (a=0; a<srckey->data.accs; a++) {
		dstkey->data.atyp[a] = srckey->data.atyp[a];
		dstkey->data.aval[a] = srckey->data.aval[a];
		dstkey->data.apos[a] = srckey->data.apos[a];
	}
	strcpy(dstkey->data.mode, srckey->data.mode);
	V3 "%s: <- %s\n",F,dmpKsig(srckey) V;
	V3 "%s: -> %s\n",F,dmpKsig(dstkey) V;
	return dstkey;
}

/* ----- find_voice ----- */
int find_voice(vid,new)
	char  vid[];
	int  *new;
{	char *F = "find_voice";
	int v;
	Ksig *ks = 0;
	for (v = 0; v < nvoice; v++) {
		V6 "%s: Is voice v=%d new?\n",F,v V;
		if (!strcmp(vid,voice[v].id)) {
			V6 "%s: Old voice v=%d.\n",F,v V;
			*new = 0;
			return v;
		}
	}
	v = nvoice;
	V6 "%s: New voice v=%d.\n",F,v V;
	if (v >= maxVc)
		rxi("Too many voices; use -maxv to increase limit, now ",maxVc);

	strcpy(voice[v].id,	vid);
	strcpy(voice[v].name,  "");
	strcpy(voice[v].sname, "");
	voice[v].stems   = 0;
	voice[v].staves  = 0;
	voice[v].brace   = 0;
	voice[v].bracket = 0;
	voice[v].do_ach  = 1;
	voice[v].select  = 1;
	voice[v].sep	 = 0.0;
	voice[v].meter   = default_meter;
	voice[v].nsym	= 0;
	V3 "%s: Voice %d use default_key is %s\n",F,v,dmpKsig(&default_key) V;
	voice[v].key = asgnKsig(voice[v].key,&default_key);
	lastks = ks = voice[v].key;
	V3 "%s: Voice[%d].key is %s from default_key.\n",F,v,dmpKsig(ks) V;
	nvoice++;
	*new = 1;
	V3 "%s: Made new voice v=%d with id \"%s\"\n",F, v,voice[v].id V;
	return v;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* switch_voice: read spec for a voice, return voice number
*/
int switch_voice (str)
	char str[];
{	char *F = "switch_voice";
	int   j, np, new, octadj=0, clfadj=0;
	int   middle='B';
	int   options=0;
	char  c, *e, *r, *q;
	char *t0, t1[501], t2[501];
	char *t1z = t1+500;	// Last byte of t1 [jc]
	char *t2z = t2+500;	// Last byte of t2 [jc]

	V5 "%s: str={%s}\n",F,str V;
	if (!do_this_tune) return 0;
	j = -1;
	/* start loop over voice options: parse t1 = t2 */
	r = str;	// Pointer to curr char in voice def string
	np = 0;
	for (;;) {
		V5 "%s: Parse r={%s} [for (;;) start]\n",F,r V;
		while (isspace(*r)) r++;
		if (*r == '\0') break;
		t0 = r;
		strcpy(t1,"");
		strcpy(t2,"");
		q = t1;
		while (*r != ' ' && *r != '\0' && *r != '=' && q < t1z) { *q = *r; r++; q++; }
		*q = '\0';
		V5 "%s: t1=\"%s\"\n",F,t1 V;
		if (*r == '=') {
			V5 "%s: t1={%s} t2={%s} [case '=' start]\n",F,t1,t2 V;
			V5 "%s: r+1={%s}\n",F,r+1 V;
			r++;
			q = t2;
			if (*r == '"') {
				r++;
				while (*r != '"' && *r != '\0' && q < t2z) { *q = *r; r++; q++; }
				if (*r == '"') r++;
			} else {
				V5 "%s: t1={%s} t2={%s} [case !'=' start]\n",F,t1,t2 V;
				V5 "%s: r={%s}\n",F,r V;
				while (*r != ' ' && *r != '\0' && q < t2z) { *q = *r; r++; q++; }
				V5 "%s: t1={%s} t2={%s} [case !'=' end]\n",F,t1,t2 V;
			}
			*q = '\0';
			V5 "%s: t1={%s} t2={%s} [case '=' end]\n",F,t1,t2 V;
		}
		np++;
		V5 "%s: t2=\"%s\" np=%d.\n",F,t2,np V;
/*
* interpret the parsed option. First case is identifier.
*/
		if (np == 1) {
			j = find_voice(t1,&new);
			lastks = voice[j].key;
		} else {						 /* interpret option */
			++options;
			V6 "%s: Option t1=\"%s\" t2=\"%s\"\n",F,t1,t2 V;
			if (j<0) V1 "%s: j=%d invalid in switch_voice",F,j V;
			lastks = voice[j].key;
			if (!strcmp(t1,"name")         || !strcmp(t1,"nm"))  {strcpy(voice[j].name,  t2);
			} elsif (!strcmp(t1,"sname")   || !strcmp(t1,"snm")) {strcpy(voice[j].sname, t2);
			} elsif (!strcmp(t1,"short")                       ) {strcpy(voice[j].sname, t2);
			} elsif (!strcmp(t1,"staves")  || !strcmp(t1,"stv")) {voice[j].staves  = atoi(t2);
			} elsif (!strcmp(t1,"brace")   || !strcmp(t1,"brc")) {voice[j].brace   = atoi(t2);
			} elsif (!strcmp(t1,"bracket") || !strcmp(t1,"brk")) {voice[j].bracket = atoi(t2);
			} elsif (!strcmp(t1,"achords") || !strcmp(t1,"ach")) {g_logv (str,t2,&voice[j].do_ach);	// New term
			} elsif (!strcmp(t1,"gchords") || !strcmp(t1,"gch")) {g_logv (str,t2,&voice[j].do_ach);	// Deprecated
			} elsif (!strcmp(t1,"annotation") || !strcmp(t1,"ann")) {g_logv (str,t2,&voice[j].do_ach);
			/*
			* for sspace: add 2000 as flag if not incremental
			*/
			} elsif (!strcmp(t1,"space") || !strcmp(t1,"spc")) {
				g_unum (str,t2,&voice[j].sep);
				if (t2[0] != '+' && t2[0] != '-') voice[j].sep += 2000.0;
			} elsif (!strcmp(t1,"clef") || !strcmp(t1,"cl")) {
				octadj = clfadj = 0;
				V5 "%s: t2={%s}\n",F,t2 V;
				e = t2 + strlen(t2) - 1;	// Check for commas or apostrophes
				while ((c = *e) && (c == ',' || c == '\'')) {
					if (c ==  ',') octadj += 7; else
					if (c == '\'') octadj -= 7;
					*e-- = 0;
				}
				V4 "%s: clfadj=%d octadj=%d '%c'\n",F,clfadj,octadj,c V;
				V4 "%s: t2={%s}\n",F,t2 V;
				if	(!strcmp(t2,"bass"))	  {lastks->data.clef = BASS;	 clfadj = -2; middle = 'd';}
				elsif (!strcmp(t2,"bass+8"))	{lastks->data.clef = BASSu8;   clfadj = -2; middle = 'd';}
				elsif (!strcmp(t2,"bass-8"))	{lastks->data.clef = BASSd8;   clfadj = -2; middle = 'd';}
				elsif (!strcmp(t2,"bass+16"))   {lastks->data.clef = BASS;	 clfadj = -2; middle = 'd';}
				elsif (!strcmp(t2,"bass-16"))   {lastks->data.clef = BASS;	 clfadj = -2; middle = 'd';}
				elsif (!strcmp(t2,"alto"))	  {lastks->data.clef = ALTO;	 clfadj = -1; middle = 'c';}
				elsif (!strcmp(t2,"alto+8"))	{lastks->data.clef = ALTOu8;   clfadj = -1; middle = 'c';}
				elsif (!strcmp(t2,"alto-8"))	{lastks->data.clef = ALTOd8;   clfadj = -1; middle = 'c';}
				elsif (!strcmp(t2,"alto+16"))   {lastks->data.clef = ALTO;	 clfadj = -1; middle = 'c';}
				elsif (!strcmp(t2,"alto-16"))   {lastks->data.clef = ALTO;	 clfadj = -1; middle = 'c';}
				elsif (!strcmp(t2,"none"))	  {lastks->data.clef = NOCLEF;   clfadj =  0;}
				elsif (!strcmp(t2,"treble"))	{lastks->data.clef = TREBLE;   clfadj =  0; middle = 'B';}
				elsif (!strcmp(t2,"treble+8"))  {lastks->data.clef = TREBLEu8; clfadj =  0; middle = 'B';}
				elsif (!strcmp(t2,"treble-8"))  {lastks->data.clef = TREBLEd8; clfadj =  0; middle = 'B';}
				elsif (!strcmp(t2,"treble+16")) {lastks->data.clef = TREBLE;   clfadj =  0; middle = 'B';}
				elsif (!strcmp(t2,"treble-16")) {lastks->data.clef = TREBLE;   clfadj =  0; middle = 'B';}
				elsif (!strcmp(t2,"tenor"))	 {lastks->data.clef = TENOR;	clfadj =  0; middle = 'B';}
				else wng("Unknown clef in voice spec: ",t2);
				V6 "%s: clfadj=%d octadj=%d middle='%c'.\n",F,clfadj,octadj,middle V;
				lastks->data.middle = middle;
				lastks->data.clfadj = clfadj;
				lastks->data.octadj = octadj;
				lastks->data.pitadj = octadj + clfadj;
				V6 "%s: Voice %d clfadj=%d octadj=%d pitadj=%d Ksig %s\n",
					F, j, lastks->data.clfadj, lastks->data.octadj,
					lastks->data.pitadj, dmpKsig(lastks) V;
			} elsif (!strcmp(t1,"stems") || !strcmp(t1,"stm")) {
				if	(!strcmp(t2,"up"))	voice[j].stems =  1;
				elsif (!strcmp(t2,"down"))  voice[j].stems = -1;
				elsif (!strcmp(t2,"free"))  voice[j].stems =  0;
				else wng("Unknown stem setting in voice spec: ",t2);
			} elsif (!strcasecmp(t1,"middle") || !strcasecmp(t1,"m")) {
				int k=0, c1=0, c2=0;
				V4 "%s: Recognized middle \"%s\" with t2=\"%s\"\n",F,t1,t2 V;
				octadj = clfadj = k = 0;
				if ((c1 = t2[k]) && (('A' <= c1 && c1 <= 'G') || ('a' <= c1 && c1 <= 'g'))) {
					middle = c1;
					V5 "%s: Found middle note '%c'\n",F,middle V;
					++k;
					clfadj = middleadj(middle);
					V5 "%s: clfadj=%d after '%c' note.\n",F,clfadj,middle V;
					while ((c2 = t2[k]) && ((c2 == ',') || ( c2 == '\''))) {
						if (c2 ==  ',') octadj += 7;
						if (c2 == '\'') octadj -= 7;
						V5 "%s: octadj=%d clfadj=%d after '%c' octave.\n",F,octadj,clfadj,c2 V;
						++k;
					}
					lastks->data.middle = middle;
					lastks->data.clfadj = clfadj;
					lastks->data.octadj = octadj;
					lastks->data.pitadj = clfadj + octadj;
					V6 "%s: Voice %d clfadj=%d octadj=%d pitadj=%d.\n",
						F,j,lastks->data.clfadj,lastks->data.octadj,lastks->data.pitadj V;
				} else {
					V1 "%s: Unrecognized note '%c' in \"%s\"\n",F,c1,t2 V;
				}
				V4 "%s: Voice %d Ksig %s (line %d)\n",F,j,dmpKsig(lastks),__LINE__ V;
			} elsif (!strcmp(t1,"treble,") || !strcmp(t1,"tenor")) {
				V6 "%s: TREBLE clef\n",F V;
				lastks->data.clef   = TREBLE;		// Was TREBLEd8 [jc]
				lastks->data.octadj = 0;
				lastks->data.clfadj = 0;
				lastks->data.pitadj = middleadj(lastks->data.middle = 'B');
				V4 "%s: Voice %d key %s (line %d)\n",F,j,dmpKsig(lastks),__LINE__ V;
			} elsif (!strcmp(t1,"alto,"))	{
				V6 "%s: ALTO clef\n",F V;
				lastks->data.clef   = ALTO;
				lastks->data.octadj = 0;
				lastks->data.pitadj =
				lastks->data.clfadj = middleadj(lastks->data.middle = 'c');
				V4 "%s: Voice %d key %s (line %d)\n",F,j,dmpKsig(lastks),__LINE__ V;
			} elsif (!strcmp(t1,"bass,,"))	{
				V6 "%s: BASS clef\n",F V;
				lastks->data.clef   = BASS;
				lastks->data.octadj = 0;
				lastks->data.pitadj =
				lastks->data.clfadj = middleadj(lastks->data.middle = 'd');
				V4 "%s: Voice %d key %s (line %d)\n",F,j,dmpKsig(lastks),__LINE__ V;
			} else {
				wng("Unknown option in voice spec: ",t1);
			}
		}
		V5 "%s: Parse r={%s} [for (;;) end]\n",F,r V;
	}
	V6 "%s: options=%d middle='%c' key=%s\n",F,options,middle,dmpKsig(voice[j].key0) V;
	if (!voice[j].key && !options) {		// If no keysig options specified, use default
		lastks = voice[j].key;
		V6 "%s: Set voice[%d].key to default_key.\n",F,j V;
		asgnKsig(lastks,&default_key);
		V6 "%s: Voice %d adjust: clef=%d octave=%d pit=%d.\n",
			F,j,lastks->data.clfadj,lastks->data.octadj,lastks->data.pitadj V;
	}
/* if new voice was initialized, save settings im meter0, key0 */
	if (new) {
		V6 "%s: New voice %d.\n",F,j V;
		voice[j].meter0 = voice[j].meter;
//		voice[j].key0   = voice[j].key;
		V6 "%s: voice[%d].key is %s (Line %d)\n",F,j,dmpKsig(lastks),__LINE__ V;
		voice[j].key0 = asgnKsig(voice[j].key0,lastks);
		V6 "%s: voice[%d].key0 is %s\n",F,j,dmpKsig(voice[j].key0) V;
	}

	V3 "%s: Switch to voice %d  <%s> <%s> <%s> clef=%d ksig %s\n",F,
		j,voice[j].id,voice[j].name,voice[j].sname,
		lastks->data.clef,dmpKsig(voice[j].key0) V;

	nsym0 = voice[j].nsym;  /* set nsym0 to decide about eoln later.. ugly */
	return j;
}

/* ----- info_field: identify info line, store in proper place  ---- */
/* switch within_block: either goes to default_info or info.
*	 Only xref ALWAYS goes to info.
*
* 2000-05-31 jc
*   Long series of if tests rewritten as a switch.
*/
int info_field(str)
	char* str;
{	char* F = "info_field";
	char  t[STRL];
	char *p;
	Info* inf;
	int   i;
	V3 "%s: Info \"%s\"\n",F,str V;
	for (i = 0;i<strlen(str);i++) {
		if (str[i] == '%') str[i] = '\0';
	}
	V3 "%s: \"%s\" (db=%d vb=%d)\n",F,str,db,vb V;

	if (within_block) {
		V3 "%s: Writing to info.\n",F V;
		inf = &info;
	} else {
		V3 "%s: Writing to default_info.\n",F V;
		inf = &default_info;
	}
	if (strlen(str)<2) return 0;
	if (str[1] != ':') return 0;

	switch (str[0]) {
	case '|':		/* |: at start of music line */
		V3 "%s: Initial |: [start repeat]",F V;
		return 0;	/* It's not an info line */
	case ':':
		V3 "%s: Initial :: [double repeat]",F V;
		return 0;
	case 'X':
		strip(info.xref, str+2);
		xrefnum = get_xref(info.xref);
		strip(inf->orig, ""); V2 "%s/X: Set inf->orig to \"%s\"\n",F,inf->orig V;
		return XREF;
	case 'A':
		strip(inf->area, str+2);
		break;
	case 'B':
		if (inf->nbook >= NBOOK) {
			wng("Too many B: book lines","");
		} else {
			strip(inf->book[inf->nbook],str+2);
			inf->nbook++;
		}
		break;
	case 'C':
		if (inf->ncomp >= NCOMP) {
			wng("Too many C: composer lines","");
		} else {
			strip(inf->comp[inf->ncomp],str+2);
			inf->ncomp++;
		}
		break;
	case 'D':
		strip(inf->disc, str+2);
		add_text(str+2, TEXT_D);
		break;
	case 'F':
		V3 "%s: F header line ignored.\n",F V;
		break;
	case 'G':
		strip(inf->group, str+2);
		break;
	case 'H':
		strip(inf->hist, str+2);
		add_text (str+2, TEXT_H);
		return HISTORY;
	case 'W':
		add_text (str+2, TEXT_W);
		return WORDS;
	case 'I':
		strip(inf->info, str+2);
		break;
	case 'K':
		V5 "%s: K field \"%s\"\n",F,str V;
		strip(inf->key, str+2);
		V5 "%s: Key signature \"%s\"\n",F,str+2 V;
		return KEY;
	case 'L':
		strip(inf->len, str+2);
		return DLEN;
	case 'M':
		strip(inf->meter, str+2);
		return METER;
	case 'N':
		strip(inf->notes, str+2);
		add_text (str+2, TEXT_N);
		break;
	case 'O':
		strip(inf->orig, str+2); V2 "%s/O: Set inf->orig to \"%s\"\n",F,inf->orig V;
		break;
	case 'R':
		strip(inf->rhyth, str+2);
		break;
	case 'P':
		strip(inf->parts, str+2);
		return PARTS;
	case 'S':
		strip(inf->src, str+2);
		break;
	case 'T':
		strip(t, str+2);
		numtitle++;
		if (numtitle>6) numtitle = 6;
		if (numtitle == 1)	  trim_title (inf->title,  t);
		elsif (numtitle == 2) trim_title (inf->title2, t);
		elsif (numtitle == 3) trim_title (inf->title3, t);
		elsif (numtitle == 4) trim_title (inf->title4, t);
		elsif (numtitle == 5) trim_title (inf->title5, t);
		elsif (numtitle == 6) trim_title (inf->title6, t);
		return TITLE;
	case 'U':
		V4 "%s: Macro ...\n",F V;
		return MACRO;
	case 'V':
		V4 "%s: Voice ...\n",F V;
		strip(lvoiceid, str+2);
		return VOICE;
	case 'Z':
		strip(inf->trans, str+2);
		add_text (str+2, TEXT_Z);
		break;
	case 'Q':
		strip(inf->tempo, str+2);
		return TEMPO;
	case 'E':
	case 'w':
		return 0;	// Tells caller that there's more parsing to do.
	default:
		V1 "Unknown header line \"%s\"\n",str V;
		return 0;
	}
	return INFO;
}

/* ----- append_meter: add meter to list of symbols -------- */
void append_meter (meter)
Msig meter;
{
	int kk;

	kk = add_sym(TIMESIG);
	zeroSym(&symv[ivc][kk],"symv/append_meter");
	symv[ivc][kk].type = TIMESIG;
	symv[ivc][kk].u	= meter.meter1;
	symv[ivc][kk].v	= meter.meter2;
	symv[ivc][kk].w	= meter.mflag;
	strcpy(symv[ivc][kk].ach,meter.top);

}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Allocate a new Ksig struct, and add it to the end of the list
*/
Ksig * newKsig(
	char *C)	/* Our caller */
{	char *F = "newKsig";
	Ksig *ks = 0;	/* Return value */
	if (!(ks = (Ksig*)Malloc(sizeof(Ksig),"Ksig"))) {
		V1 "%s: Can't get %lu bytes for new Ksig (from %s)\n",F,sizeof(Ksig),C V;
		return 0;
	}
	V5 "%s: Got %lX from Malloc(%lu)\n",F,(ulong)ks,sizeof(Ksig) V;
	bzero(ks,sizeof(Ksig));	/* Make sure it's clean */
//	ksiglast->next = ks;	/* Insert at end of list */
//	ks->knum = ++ksignum;	/* Bump the Ksig counter */
//	ksiglast = ks;			/* New end of list */
	V6 "%s: New %s\n",F,dmpKsig(ks) V;
	return ks;
}

/* ----- append_key_change: append change of key to sym list ------ */
void append_key_change(oldkey,newkey)
	Ksig *oldkey;
	Ksig *newkey;
{	char *F = "append_key_change";
	int   a, accs, n1,n2,t1,t2,kk;
	Ksig *ks;
	V6 "%s: Called.\n",F V;
	V6 "%s: Old %s\n",F,dmpKsig(oldkey) V;
	V6 "%s: New %s\n",F,dmpKsig(newkey) V;
	if (oldks) {
//		V3 "%s: Using old ksig code.\n",F V;
//		n1 = oldkey->data.sf;
//		t1 = A_SH;
//		if (n1 < 0) {
//			n1 = -n1;			/* Number of flats */
//			t1 = A_FT;			/* Type is flats */
//		}
	} else {
		V3 "%s: Using new ksig code.\n",F V;
		n1 = oldkey->data.accs;	/* Number of accidentals */
		t1 = A_MX;				/* Keysig type */
	}
	V6 "%s: n1=%d t1=%d.\n",F,n1,t1 V;
	if (oldks) {
//		n2 = newkey->data.sf ? newkey->data.sf : newkey->data.sf;
//		t2 = A_SH;
//		if (n2 < 0) {
//			n2 = -n2;			/* Number of flats */
//			t2 = A_FT;			/* Type is flats */
//		}
	} else {
		n2 = newkey->data.accs;	/* Number of accidentals */
		t2 = A_MX;				/* Keysig type */
	}
	V6 "%s: n2=%d t2=%d.\n",F,n2,t2 V;
	if (newkey->data.clef != oldkey->data.clef) {	  /* clef change */
		kk = add_sym(CLEF);
		symv[ivc][kk].u = newkey->data.clef;
		symv[ivc][kk].v = 1;
		symv[ivc][kk].clef = newkey->data.clef;
		V3 "%s: Clef change for symv[%d][%d] to u=clef=%d v=1.\n",F,ivc,kk,newkey->data.clef V;
	}
//	if (oldks && t1 == t2 && t1 != A_MX) {	/* here if old and new have same type */
//		V6 "%s: Same type %d, change from %d to %d accidentals.\n",F,t1,n1,n2 V;
//		if (n2 > n1) {					/* more new symbols ..*/
//			kk = add_sym(KEYSIG);		/* draw all of them */
//			ks = symv[ivc][kk].ksig = newKsig(F);
//			copyKsig(newkey,ks);
//			symv[ivc][kk].u = 1;		/* Clef type */
//			symv[ivc][kk].acc1 =  n2;
//			symv[ivc][kk].acc2 = 100;
//			symv[ivc][kk].v = -1;		/* Was n2 */
//			symv[ivc][kk].w = -1;		/* Was 100 */
//			symv[ivc][kk].t = t1;
//			symv[ivc][kk].q = newkey->data.sf;
//		} elsif (n2 < n1) {			/* less new symbols .. */
//			kk = add_sym(KEYSIG);		/* draw all new symbols and neutrals */
//			ks = symv[ivc][kk].ksig = newKsig(F);
//			copyKsig(newkey,ks);
//			symv[ivc][kk].u = 1;		/* Clef type */
//			symv[ivc][kk].acc1 = n1;
//			symv[ivc][kk].acc2 = n2+1;
//			symv[ivc][kk].v = -1;		/* Was n1 */
//			symv[ivc][kk].w = -1;		/* Was n2+1 */
//			symv[ivc][kk].t = t2;
//			symv[ivc][kk].q = newkey->data.sf;
//		} elsif (n1 == n2) {
//			V6 "%s: n1=%d == n2=%d.\n",F,n1,n2 V;
//			kk = add_sym(KEYSIG);		/* draw all new symbols and neutrals */
//			ks = symv[ivc][kk].ksig = newKsig(F);
//			copyKsig(newkey,ks);
//			symv[ivc][kk].u = 1;		/* Clef type */
//			symv[ivc][kk].acc1 = n1;
//			symv[ivc][kk].acc2 = n2+1;
//			symv[ivc][kk].v = -1;		/* Was n1 */
//			symv[ivc][kk].w = -1;		/* Was n2+1 */
//			symv[ivc][kk].t = t2;
//			symv[ivc][kk].q = newkey->data.sf;
//		}
//	} else {  						   /* here for change s->f or f->s */
		V6 "%s: New type ksig %d->%d, accidentals %d->%d.\n",F,t1,t2,n1,n2 V;
		if (ksigcancel) {
			kk = add_sym(KEYSIG);			/* neutralize all old symbols */
			symv[ivc][kk].t = A_NT;			/* Change accidental type to natural */
			symv[ivc][kk].u = 1;
			symv[ivc][kk].u = 1;
			symv[ivc][kk].acc1 = n1;
			symv[ivc][kk].acc2 = 1;
			if (!(ks = symv[ivc][kk].ksig)) {
				V6 "%s: Alloc Ksig structure for voice %d.\n",F,ivc V;
				if (!(ks = symv[ivc][kk].ksig = newKsig(F))) {
					V1 "%s: ### OUT OF MEMORY (can't get Ksig) ###\n",F V;
					return;
				}
				copyKsig(oldkey,ks);
			}
			V6 "%s: symv[%d][%d].ksig [%s] exists.\n",F,ivc,kk,dmpKsig(ks) V;
			ks->data.accs = n1;
			ks->data.accs = 0;
			ks->data.clef = oldkey->data.clef;
			accs = fillKsig(ks,n1);
			for (a=0; a<n1; a++) {
				ks->data.atyp[a] = A_NT;
				ks->data.aval[a] = oldkey->data.aval[a];
				ks->data.apos[a] = oldkey->data.apos[a];
				V6 "%s: symv[%d][%d].atyp[%d]=%d=A_NT aval=%c apos=%d.\n",F
					,ivc,kk,a,A_NT,ks->data.aval[a],ks->data.apos[a] V;
			}
			V3 "%s: Old symv[%d][%d].ksig = [%s]\n",F,ivc,kk,dmpKsig(symv[ivc][kk].ksig) V;
			symv[ivc][kk].v = -1;			/* Was n1 */
			symv[ivc][kk].w = -1;			/* Was 1 */
			symv[ivc][kk].t = t1;
		}
		kk = add_sym(KEYSIG);			/* add all new symbols */
		if (!(ks = symv[ivc][kk].ksig)) {
			V6 "%s: symv[%d][%d] needs a ksig.\n",F,ivc,kk V;
			if (!(ks = newKsig(F))) {
				V1 "%s: ### OUT OF MEMORY (can't get Ksig) ###\n",F V;
				return;
			}
		}
		V3 "%s: Set up new %s with %d accidentals.\n",F,dmpKsig(ks),n2 V;
		copyKsig(newkey,ks);
//		ks->data = newkey->data;
		symv[ivc][kk].u = 1;
		symv[ivc][kk].acc1 = n2;
		symv[ivc][kk].acc2 = 100;
		symv[ivc][kk].v = -1;			/* Was n2 */
		symv[ivc][kk].w = -1;			/* Was 100 */
		symv[ivc][kk].t = t2;
		symv[ivc][kk].q = newkey->data.sf;
		V3 "%s: New symv[%d][%d].ksig = [%s]\n",F,ivc,kk,dmpKsig(symv[ivc][kk].ksig) V;
//	}
	V6 "%s: Done.\n",F V;
}

#if 0
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This routine calculates the number of scale steps  above  a  nominal  lowest
* note ("A,,"), adjusted for the clef and octave of the current staff.
*/
int calc_numeric_pitch(char note)
{	char *F = "calc_numeric_pitch";
	int   pitch = 0;
	if (note == 'z') {
		pitch = 14;
	} elsif (within('C',note,'G')) {
		pitch = note - 'C' + 16 + voice[ivc].key->data.octadj + voice[ivc].key->data.clfadj;
	} elsif (within('A',note,'B')) {
		pitch = note - 'A' + 21 + voice[ivc].key->data.octadj + voice[ivc].key->data.clfadj;
	} elsif (within('c',note,'g')) {
		pitch = note - 'c' + 23 + voice[ivc].key->data.octadj + voice[ivc].key->data.clfadj;
	} elsif (within('a',note,'b')) {
		pitch = note - 'a' + 28 + voice[ivc].key->data.octadj + voice[ivc].key->data.clfadj;
	} else {
		V1 "%s: numeric_pitch: cannot identify <%c>\n",F,note V;
	}
	V6 "%s: Note '%c' pitch %d adjust by oct=%d clef=%d pit=%d.\n",
		F,note,pitch,voice[ivc].key->data.octadj,voice[ivc].key->data.clfadj,voice[ivc].key->data.pitadj V;
	return pitch;
}
#endif

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* The numeric pitch of a note is the number of scale steps above  our  nominal
* lowest note.  The lowest note corresponds to "A,," which is pitch 0.
*/
int numeric_pitch(char note)
{	char *F="numeric_pitch";
	int   pitch = 0;
	if (note == 'z') {
		pitch = 14;
	} elsif (within('C',note,'G')) {
		pitch = note - 'C' + 16; // + voice[ivc].key->data.pitadj;
	} elsif (within('A',note,'B')) {
		pitch = note - 'A' + 21; // + voice[ivc].key->data.pitadj;
	} elsif (within('c',note,'g')) {
		pitch = note - 'c' + 23; // + voice[ivc].key->data.pitadj;
	} elsif (within('a',note,'b')) {
		pitch = note - 'a' + 28; // + voice[ivc].key->data.pitadj;
	} else {
		V1 "%s: cannot identify <%c>\n",F,note V;
	}
	V6 "%s: Note '%c' is pitch %d.\n",F,note,pitch V;
	return pitch;
}

/*
* ----- symbolic_pitch: translate numeric pitch back to symbol ------
*/
int symbolic_pitch(pit,str)
	int pit;
	char str[];
{	char *F="symbolic_pitch";
	int  p,r,s;
	char ltab1[7] = {'C','D','E','F','G','A','B'};
	char ltab2[7] = {'c','d','e','f','g','a','b'};

	p = pit-16;
	r = (p+700)%7;	/* Note within octave */
	s = (p-r)/7;	/* Octave */

	if (p<7) {
		sprintf (str,"%c,,,,,",ltab1[r]);
		str[1-s] = '\0';
	} else {
		sprintf (str,"%c'''''",ltab2[r]);
		str[s] = '\0';
	}
	return 0;
}

char *A2acc[] = {
	".",			/* 0 */
	"^",			/* 1 A_SH */
	"",				/* 2 A_NT */
	"_",			/* 3 A_FT */
	"^^",			/* 4 A_DS */
	"__",			/* 5 A_DF */
	"_^",			/* 6 A_MX */
	"^/",			/* 7 A_HSH */
	"_/",			/* 8 A_HFT */
	"?",			/* 9 */
};

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Produce a printable dump string for a key signature.
*/
char *dmpKsig(
	Ksig *ks)
{	int   a;
	char *bp;
static char ksbuf[1000];
	if (!ks) {return "[[NULL]]";}
	sprintf(ksbuf,"Ksig %08lX: [",(ulong)ks);
	bp = ksbuf + strlen(ksbuf);
//	if (ks->data.root) {
		*bp++ = 'A' + ks->data.root;
		if (ks->data.rootAc) {
				if (ks->data.rootAc == A_HSH ||
				ks->data.rootAc == A_HFT)
			  *bp++ = '/';
			*bp++ = (ks->data.rootAc == A_SH | ks->data.rootAc == A_HSH) ? '#'
				  : (ks->data.rootAc == A_FT | ks->data.rootAc == A_HFT) ? 'b'
				  : (ks->data.rootAc == A_NT) ? ' '
				  : '?';
		}
		if (ks->data.mode) {
			sprintf(bp,"%s",ks->data.mode);
			bp += strlen(bp);
		}
		*bp++ = ' ';
		*bp = 0;
//	}
	sprintf(bp,"%s accs=%d ",SymClef(ks->data.clef),ks->data.accs);
	bp += strlen(bp);
	if (ks->data.clfadj || ks->data.octadj || ks->data.pitadj || ks->data.middle) {
		sprintf(bp,"adj:c=%d/o=%d:p=%d:'%c' ",
			ks->data.clfadj,ks->data.octadj,ks->data.pitadj,ks->data.middle);
		bp += strlen(bp);
	}
	for (a = 0; a < ks->data.accs; a++) {
		sprintf(bp,"%s",A2acc[ks->data.atyp[a]]);
		bp += strlen(bp);
		*bp++ = ks->data.aval[a];
	}
	*bp++ = ']';
	*bp = 0;
	return ksbuf;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Fill in a Ksig struct with the standard key signature for ks sharps or flats.
*/
int fillKsig(
	Ksig *ks,		/* Ksig struct */
	int   sf)		/* Sharps or flats */
{	char *F = "fillKsig";
	int   i;
	if (sf > 0) {
		V5 "%s: Key has %d sharps.\n",F,sf V;
		for (i = 0; i<sf; i++) {
			ks->data.atyp[i] = A_SH;
			ks->data.aval[i] = sh_val[i];
			ks->data.apos[i] = note_pos(ks->data.aval[i],ks->data.clef);
			V6 "%s: Acc %d atyp=%d=A_SH apos=%d aval='%c'\n",F,i,ks->data.atyp[i],ks->data.apos[i],ks->data.aval[i] V;
		}
		ks->data.accs = sf;
	} elsif (sf < 0) {
		V5 "%s: Key has %d flats.\n",F,-sf V;
		for (i = 0; i<-sf; i++) {
			ks->data.atyp[i] = A_FT;
			ks->data.aval[i] = fl_val[i];
			ks->data.apos[i] = note_pos(ks->data.aval[i],ks->data.clef);
			V6 "%s: Acc %d atyp=%d=A_FT apos=%d aval='%c'\n",F,i,ks->data.atyp[i],ks->data.apos[i],ks->data.aval[i] V;
		}
		ks->data.accs = -sf;
	} else {
		ks->data.accs = 0;
	}
	V6 "%s: Ksig=%lX has sf=%d accs=%d.\n",F,(ulong)ks,sf,ks->data.accs V;
	return ks->data.accs;
}

static Ksig savekey;
static Ksig newksig;

/* ----- handle_inside_field: act on info field inside body of tune --- */
void handle_inside_field(type)
	int type;
{	char *F = "handle_inside_field";
	int rc;
	V6 "%s: Called for type %d.\n",F,type V;
	if (type == METER) {
		if (nvoice == 0) ivc = switch_voice(DEFVOICE);
		set_meter(info.meter,&voice[ivc].meter);
		append_meter(voice[ivc].meter);
	} elsif (type == DLEN) {
		if (nvoice == 0) ivc = switch_voice(DEFVOICE);
		set_dlen (info.len,  &voice[ivc].meter);
	} elsif (type == KEY) {
		V5 "%s: Key signature in nvoice %d.\n",F,nvoice V;
		if (nvoice == 0) {
			ivc = switch_voice(DEFVOICE);
		}
//		savekey = *voice[ivc].key;
		V6 "%s: voice[%d].key is %s\n",F,ivc,dmpKsig(voice[ivc].key) V;
		asgnKsig(&savekey,voice[ivc].key);
		V6 "%s: savekey is %s\n",F,dmpKsig(&savekey) V;
		if (!voice[ivc].key) voice[ivc].key = newKsig(F);
		V6 "%s: Parse key \"%s\" into voice[%d].key = %s\n",F,info.key,ivc,dmpKsig(voice[ivc].key) V;
		rc = parseKey(info.key,voice[ivc].key,0);
		V5 "%s: after parseKey(info.key,...)\n",F V;
		V6 "%s: Voice %d has %s\n",F,ivc,dmpKsig(voice[ivc].key) V;
		V6 "%s: Saved %s\n",F,dmpKsig(&savekey) V;
		if (rc) {
			V6 "%s: Call set_transtab for %d halftones voice %d.\n",F,halftones,ivc V;
			set_transtab(halftones,voice[ivc].key);
		}
		V6 "%s: Call append_key_change for savekey, voice[%d].key\n",F,ivc V;
		append_key_change(&savekey,voice[ivc].key);
	} elsif (type == VOICE) {
		ivc = switch_voice (lvoiceid);
	}
}

/* ----- prsUInt: parse for unsigned integer ----- */
int prsUInt()
{	char *F="prsUInt";
	int number=0, ndig;
//	char num[21];

	if (!isdigit(*P)) return 0;
	ndig = 0;
	while (isdigit(*P)) {
//		num[ndig] = *P;
		ndig++;
//		num[ndig] = 0;
		number = (number * 10) + (*P - '0');
		P++;
	}
//	sscanf (num, "%d", &number);
	V3 "%s: parsed unsigned int %d\n",F,number V;
	return number;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* prsBar: parse for some kind of bar.  We eat up the bar-line chars, and also
* any ending label that may be present.
*/
int prsBar()
{	char *F="prsBar";
	int   c, i, k, r;
	int   ngr=0,pgr[30],agr[30];
	Sym  *sp;

	V3 "%s: Called at <%s>\n",F,P V;
//	ngr = prsGrSeq(pgr,agr); /* grace notes */
//	while ((c = *P) && isspace(c)) ++P;
	V3 "%s: Bar sym has %d grace notes at <%s>.\n",F,ngr,P V;
	prsDeco();	// Allow decorations and annotations on par lines
	prsAch();	// Allow "chords" on bar lines

	// special cases: [ and digit or quote without a preceeding bar, [ ending
	if (*P == '[') {
		V3 "%s: [\n",F V;
		if (((c = P[1]) && (isdigit(c))) || (c=='"')) {
			V3 "%s: [%c\n",F,c V;
			k = add_sym(BAR);
			symv[ivc][k].u = B_INVIS;
			V3 "%s: symv[%d][%d] made invisible.\n",F,ivc,k V;
//			++P;
			prsEnd(k);
			V3 "%s: Return 1.\n",F V;
			return 1;
		}
	}
	if ((*P == '[') && (*(P+1) == '|') && (*(P+2) == ':')) {
		V3 "%s: [|: reduced.\n",F V;
		++P;		// Ignore spurious '['
	}
	/* identify valid standard bar types */
	if (*P == '|') {
		k = add_sym(BAR);
		symv[ivc][k].u = B_SNGL;
		P++;
		while (isspace(*P)) ++P;
		while (*P == '|') {		// Eat up any number of bar lines
			symv[ivc][k].u = B_DBL;
			P++;
		}
		if (*P == ':') {		// Start repeat
			symv[ivc][k].u = B_LREP;
			while (*P == ':') {		// Count the colons [jc]
				++symv[ivc][k].lrep;	// Number of colons found
				P++;
			}
			V3 "%s: symv[ivc=%d][k=%d].lrep=%d.\n",F,ivc,k,symv[ivc][k].lrep V;
			V3 "%s: Found bar line with %d left repeats.\n",F,symv[ivc][k].lrep V;
		} elsif (*P == ']') {				  /* code |] for fat end bar */
			symv[ivc][k].u = B_FAT2;
			P = P+1;
			if (*P == '|') {				/* |]| for for fat middle bar */
				symv[ivc][k].u = B_FAT3;
				P++;
			}
		} elsif (*P == '[' && P[1] == '|') {	/* code |[| for fat middle bar */
			symv[ivc][k].u = B_FAT3;
			P += 2;
		}
	} elsif (*P == ':') {		// End repeat
		r = 0;
		while (*P == ':') {		// Count the colons [jc]
			r++;				/* Number of colons found */
			P++;
		}
		V3 "%s: Found r=%d colons.\n",F,r V;
		if (r == 2 && *P != '|') {
			V3 "%s: :: (r=%d)\n",F,r V;
			k = add_sym(BAR);
			symv[ivc][k].u = B_DREP;
			symv[ivc][k].lrep = symv[ivc][k].lrep = 0;
		} elsif (*P == '|') {
			V3 "%s: Found bar line after %d colons.\n",F,r V;
			k = add_sym(BAR);
			symv[ivc][k].u = B_RREP;
			symv[ivc][k].lrep = r;
			P++;
			while (*P == ']') {	// Eat up ] fat bars
				V3 "%s: ] ignored (r=%d)\n",F,r V;
				++P;
			}
//			if (*P == ']') {	// Ignore spurious ']'
//				V3 "%s: :|] reduced.\n",F,r V;
//				++P;
//			}
		} else {
			V3 "%s: Found no bar line after r=%d colons.\n",F,r V;
			k = add_sym(BAR);
			symv[ivc][k].u = B_DREP;
			symv[ivc][k].lrep = symv[ivc][k].rrep = r;
			P++;
		}
	} elsif ((*P == '[') && (*(P+1) == '|') && (*(P+2) == ']')) {  /* code [|] invis */
		k = add_sym(BAR);
		symv[ivc][k].u = B_INVIS;
		P = P+3;
	} elsif ((*P == '[') && (*(P+1) == '|')) {	/* code [| for thick-thin bar */
		k = add_sym(BAR);
		symv[ivc][k].u = B_FAT1;
		P = P+2;
		while (isspace(*P)) ++P;
		while (*P == '|') {	// Ignore extra '|'
			V3 "%s: [|| reduced (r=%d)\n",F,r V;
			++P;
		}
	} else {
		V3 "%s: Return 0.\n",F V;
		return 0;
	}
	V3 "%s: Bar symv[%d][%d] has %d grace notes.\n",F,ivc,k,ngr V;
	sp = &symv[ivc][k];
	sp->gr.n = ngr;			/* Grace note count */

	copyDecosToSym (sp);
	clearDecos ();
	V3 "%s: Bar symv[%d][%d] has %d decorations and %d grace notes.\n",F,ivc,k,sp->dc.n,sp->gr.n V;
	for (i = 0;i<ngr;i++) {
		sp->gr.p[i] = pgr[i];
		sp->gr.a[i] = agr[i];
	}
	prsEnd(k);
/*
* Add the accompaniment chord to the text.
*/
	symv[ivc][k].ach[0] = 0;
	if (strlen(ach)>0) {
		V3 "%s: ach=\"%s\"\n",F,ach V;
		strcat(symv[ivc][k].ach, " ");
		strcat(symv[ivc][k].ach, ach);
		V3 "%s: symv[%d][%d].ach=\"%s\"\n",F,ivc,k,symv[ivc][k].ach V;
		ach[0] = 0;		/* Make sure we don't use it twice */
	}
/*
* If we found an annotation before the bar line, add the annotation to the bar's text.
*/
	if (ann && ann->t && (ann->l > 0)) {
		V3 "%s: Init annotation for voice %d sym %d.\n",F,ivc,k V;
		if (!TextInit(&symv[ivc][k].ann,64)) {
			V1 "%s: Can't initialize symv[%d][%d].ann\n",F,ivc,k V;
			V3 "%s: Return 0.\n",F V;
			return 0;
		}
		V3 "%s: ann=%08X\n",F,(uint)ann V;
		TextAppend(symv[ivc][k].ann, " ");
		TextAppend(symv[ivc][k].ann, ann->t);
		symv[ivc][k].ann->y = ann->y;
		symv[ivc][k].ann->p = ann->p;
		V3 "%s: symv[%d][%d] ann=!%s! at %6.3f pos %d.\n",F,ivc,k,TextStr(symv[ivc][k].ann),symv[ivc][k].ann->y,symv[ivc][k].ann->p V;
	}
	TextInit(&ann,64);		/* Make sure we don't use it twice */
	V3 "%s: Return 1.\n",F V;
	return 1;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Test a char for being legal in an unquoted ending label.  We  now  recognize
* only digits, commas and hyphens; you must use quotes around everything else.
*/
int isrepstr(c)
	int   c;
{	if (isdigit(c)
	|| (c == ',') 	// Default ending-label chars
	|| (c == '-')
	|| (c == 'x') 	// Someone requested 'x'; what does it mean?
	)	return 1;
	return 0;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Parse an ending string.  This eats up whatever isrepstr() considers a  valid
* (unquoted)  ending  indicator  string,  copies it to the vtxt string for the
* current symbol, and returns a pointer to the char that ended the scan.
*/
char *repstr(k,p)
	int   k;
	char *p;
{	char *F = "repstr";
	int   c, i=0;
	while (isrepstr(c = *p)) {
		symv[ivc][k].vtxt[i++] = c;
		symv[ivc][k].vtxt[i  ] = 0;	// Only needed for V message:
		V5 "%s: vtxt=\"%s\"\n",F,symv[ivc][k].vtxt V;
		p++;
	}
	symv[ivc][k].vtxt[i] = 0;
	symv[ivc][k].v = 9;		// Kludge: Old ending number must be nonzero
	V5 "%s: vtxt=\"%s\"\n",F,symv[ivc][k].vtxt V;
	return p;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Parse a quoted ending string.  This eats up characters  until  the  matching
* '"',  copies  it  to  the  vtxt string for the current symbol, and returns a
* pointer to the char after the quote that ended the scan.
*/
char *repquot(k,p)
	int   k;
	char *p;
{	char *F = "repstr";
	int   c, i=0;
	while ((c = *p) && (c != '"')) {
		symv[ivc][k].vtxt[i++] = c;
		symv[ivc][k].vtxt[i  ] = 0;	// Only needed for V message:
		V5 "%s: vtxt=\"%s\"\n",F,symv[ivc][k].vtxt V;
		p++;
	}
	if (c == '"') p++;		// Eat up the final quote
	symv[ivc][k].vtxt[i] = 0;
	symv[ivc][k].v = 9;		// Kludge: Old ending number must be nonzero
	V5 "%s: vtxt=\"%s\"\n",F,symv[ivc][k].vtxt V;
	return p;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* See if valid bar is followed by specifier for first or second ending
* Called with:
*   k == index of BAR symbol.
*   p -> first char ('[', '"' or digit) of ending label.
*
* Modified to accept any string of digits [jc 2000-04-22]
* Modified to accept quoted string [jc 2002-05-20]
*/
void prsEnd(k)
	int   k;
{	char *F = "prsEnd";
	char *q = P;		// Remember starting point
	int   c=0, d=0, i;
	int   quoted=0;		// Type of ending (quoted or not)
	V5 "%s: Parse %8.8s...\n",F,P V;
	if (!(c = *P)) return;
	while (isspace(c))	// Skip white stuff
		c = *++P;
	if (!c) return;
	V5 "%s: Parse '%c'\n",F,c V;
	if (isdigit(c)) {	// Just a digit is degenerate ending
		V5 "%s: Ending digit %02X='%c'.\n",F,c,c V;
		symv[ivc][k].v = (c - '0');
		P++;			// Skip over one digit
	} elsif (c == '[') {
		V5 "%s: Ending bracket.\n",F V;
		if (((d = P[1]) && isdigit(d))) {
			V5 "%s: [ Ending digit %02X='%c'.\n",F,d,d V;
//			P += 2;			// Skip [ and one digit
			P = repstr(k,P+1);
		} elsif ((d = P[1]) && (d == '"')) {
			V5 "%s: [ Ending quote %02X='%c'.\n",F,d,d V;
			P = repquot(k,P+2);
//			quoted = 1;		// Ending is quoted
//			P += 2;		// Skip ["
		} else {
			V5 "%s: Not an ending.\n",F V;
			return;		// It's not an ending label
		}
	} else {
		V5 "%s: Not an ending.\n",F V;
		P = q;			// Return to starting point
		return;			// It's not an ending label
	}
	V5 "%s: Continue with \"%8.8s\"...\n",F,P V;
	if ((c = *P)) {
		V5 "%s: Next repeat char %02X='%c'.\n",F,c,c V;
		if (quoted) {	// Quoted ending?
			V5 "%s: Quoted repeat syntax at %02X='%c'.\n",F,c,c V;
			P = repquot(k,P);
		} elsif (isrepstr(c)) {
			V5 "%s: Extended repeat syntax at %02X='%c'.\n",F,c,c V;
			P = repstr(k,P-1);
		}
	}
}

/* ----- parse_space: parse for whitespace ----
 * Added by JC: Ignore ` (grave accent) chars entirely.
*/
int parse_space ()
{
	int c, rc;

	rc = 0;
	while (((c = *P) && (c == ' ')) || (c == '\t') || (c == '`')) {
		if (c != '`')	// Ignore graves
			rc = 1;
		P++;
	}
	if (db>3) if (rc) fprintf(stderr,"  parsed whitespace\n");
	return rc;
}

/* ----- parse_esc: parse for escape sequence ----- */
int parse_esc ()
{	int nseq;
	char *pp;

	if (*P == '\\') {					 /* try for \...\ sequence */
		P++;
		nseq = 0;
		while ((*P != '\\') && (*P != 0)) {
			escseq[nseq] = *P;
			nseq++;
			P++;
		}
		if (*P == '\\') {
			P++;
			escseq[nseq] = 0;
			V3 "  parsed esc sequence <%s>\n", escseq V;
			return ESCSEQ;
		} else {
			if (cfmt.breakall) return DUMMY;
			V3 "  parsed esc to EOL.. continuation\n" V;
		}
		return CONTINUE;
	}

	/* next, try for [..] sequence */
	if ((*P == '[') && (*(P+1) >= 'A') && (*(P+1) <= 'Z') && (*(P+2) == ':')) {
		pp = P;
		P++;
		nseq = 0;
		while ((*P != ']') && (*P != 0)) {
			escseq[nseq] = *P;
			nseq++;
			P++;
		}
		if (*P == ']') {
			P++;
			escseq[nseq] = 0;
			V3 "  parsed esc sequence <%s>\n", escseq V;
			return ESCSEQ;
		}
		syntax("Escape sequence [..] not closed", pp);
		return ESCSEQ;
	}
	return 0;
}


/* ----- parse_nl: parse for newline ----- */
int parse_nl ()
{

	if ((*P == '\\')&&(*(P+1) == '\\')) {
		P += 2;
		return 1;
	} else {
		return 0;
	}
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Parse an accompaniment chord.
*/
int prsAch ()
{	char *F = "prsAch";
	char *q;
	int c, n;

	V3 "%s: Called for <%3.3s>\n",F,P V;
	if (*P != '"') {V5 "%s: No achord\n",F V; return 0;}

	q = P;				/* Note position of opening quote */
	P++;				/* Skip to first char of string */
	switch (c = *P) {	/* Check for position char */
	case '^':			/* Above staff */
		V3 "  Chord position '%c' above staff.\n",c V;
		P++; break;
	case '<':			/* Left of note */
		V3 "  Chord position '%c' left of note.\n",c V;
		P++; break;
	case '=':			/* Next to note */
		V3 "  Chord position '%c' next to note.\n",c V;
		P++; break;
	case '>':			/* Right of note */
		V3 "  Chord position '%c' right of note.\n",c V;
		P++; break;
	case '_':			/* Below staff */
		V3 "  Chord position '%c' below staff.\n",c V;
		P++; break;
	}
	n = strlen(ach);	/* Length of chord so far */
	if (n > 0) syntax("Overwrite unused accomp chord", q);

	while ((*P != '"') && (*P != 0)) {
		ach[n] = *P;
		n++;
		if (n >= 200) {
			syntax("String for accomp chord too long", q);
			V5 "%s: Return 1 [String for accomp chord too long].\n",F V;
			return 1;
		}
		P++;
	}
	if (*P == 0) {
		syntax("EOL reached while parsing accomp chord", q);
		V5 "%s: Return 1 [EOL reached while parsing accomp chord].\n",F V;
		return 1;
	}
	P++;
	ach[n] = 0;
	V3 "  parse accomp chord <%s>\n", ach V;

/*|   ach_transpose (voice[ivc].key); |*/

	V5 "%s: Return 1.\n",F V;
	return 1;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* prsLen: parse length specifer for note or rest
*/
int prsLen()
{	char *F="prsLen";
	int len,fac;

	len = voice[ivc].meter.dlen;		  /* start with default length */
	V5 "%s: len=%d (default)\n",F,len V;

	if (len <= 0) {
		fprintf(stderr,"!!! parse_len:  got len = %d\n", len);
	}
	if (isdigit(*P)) {				 /* multiply note length */
		fac = prsUInt ();
		if (fac == 0) fac = 1;
		len *= fac;
		V5 "%s: len=%d (*= fac=%d)\n",F,len,fac V;
	}
	if (*P == '/') {				   /* divide note length */
		while (*P == '/') {
			P++;
			if (isdigit(*P)) {
				fac = prsUInt();
			} else {
				fac = 2;
			}
			V5 "%s: fac=%d\n",F,fac V;
			if (len%fac) {
				syntax("Bad length divisor", P-1);
				return len;
			}
			len = len/fac;
			V5 "%s: len=%d (/= fac=%d)\n",F,len,fac V;
		}
	}
	V5 "%s: len=%d (result)\n",F,len V;
	return len;
}

/* ----- prsGrSeq --------- */
int prsGrSeq (pgr,agr)
	int pgr[],agr[];
{	char *F="prsGrSeq";
	char *p0;
	int   c, n,len;

	V5 "%s: Called at <%s>\n",F,P V;
	p0 = P;
	while ((c = *P) && isspace(c)) P++;
	if (c != '{') {
		V5 "%s: '%c' not a grace sequence.\n",F,c V;
		return 0;
	}
	P++;
	if (*P == '/') {	// Ignore / notation [jc 2011-12-7]
		P++;
	}

	n = 0;
	while (*P != '}') {
		if (*P == '\0') {
			V5 "%s: NULL encountered.\n",F V;
			syntax("Unbalanced grace note sequence", p0);
			return 0;
		}
		if (!isnote(*P)) {
			if (unexpsym != linenum)
				syntax("Unexpected symbol in grace note sequence", P);
			unexpsym = linenum;
			P++;
		}
		agr[n] = 0;
		if (*P == '=') agr[n] = A_NT;
		if (*P == '^') {
			if (*(P+1) == '^') { agr[n] = A_DS; P++; }
			elsif (*(P+1) == '/') { agr[n] = A_HSH; P++; }
			else agr[n] = A_SH;
		}
		if (*P == '_') {
			if (*(P+1) == '_') { agr[n] = A_DF; P++; }
			elsif (*(P+1) == '/') { agr[n] = A_HFT; P++; }
			else agr[n] = A_FT;
		}
		if (agr[n]) P++;

		pgr[n] = numeric_pitch(*P);
		P++;
		while (*P == '\'') {pgr[n] += 7; P++; if (lastks) lastks->shup++;}
		while (*P == ',')  {pgr[n] -= 7; P++; if (lastks) lastks->shdn++;}
		if (lastks) V2 "%s: shdn=%d shup=%d.\n",F,lastks->shdn,lastks->shup V;

//		do_transpose (voice[ivc].key, &pgr[n], &agr[n]);

		len = prsLen ();	/* ignore any length specifier */
		n++;
	}

	P++;
	V5 "%s: Return %d\n",F,n V;
	return n;
}

/* ----- identify_note: set head type, dots, flags for note --- */
void identify_note (s,q)
	Sym *s;
	char *q;
{	char *F = "identify_note";
	int head,base,len,flags,dots;

	if (s->len == 0) s->len = s->lens[0];
	len = s->len;

	/* set flag if duration equals length of one measure */
	if (nvoice>0) {
		V6 "%s: nvoice=%d ivc=%d WHOLE=%d.\n",F,nvoice,ivc,WHOLE V;
		V6 "%s: voice[%d].meter.meter1=%d meter2=%d.\n",F,
		ivc,voice[ivc].meter.meter1,voice[ivc].meter.meter2 V;
		if (voice[ivc].meter.meter2 > 0)
			if (len == (WHOLE*voice[ivc].meter.meter1)/voice[ivc].meter.meter2)
				s->fullmes = 1;
	}

	base = WHOLE;
	if (len >= WHOLE)			base = WHOLE;
	elsif (len >= HALF)		  base = HALF;
	elsif (len >= QUARTER)	   base = QUARTER;
	elsif (len >= EIGHTH)		base = EIGHTH;
	elsif (len >= SIXTEENTH)	 base = SIXTEENTH;
	elsif (len >= THIRTYSECOND)  base = THIRTYSECOND;
	elsif (len >= SIXTYFOURTH)   base = SIXTYFOURTH;
	else {
		V2 "%s: Cannot identify head for note, base=%d len=%d \"%s\"\n",F,base,len,q V;
		syntax("Cannot identify head for note",q);
	}
	if (base == WHOLE)	 head = H_OVAL;
	elsif (base == HALF) head = H_EMPTY;
	else				 head = H_FULL;

	if (base == SIXTYFOURTH)		flags = 4;
	elsif (base == THIRTYSECOND)  flags = 3;
	elsif (base == SIXTEENTH)	 flags = 2;
	elsif (base == EIGHTH)		flags = 1;
	else						  flags = 0;

	dots = 0;
	if (len == base)			dots = 0;
	elsif (2*len == 3*base)   dots = 1;
	elsif (4*len == 7*base)   dots = 2;
	elsif (8*len == 15*base)  dots = 3;
	else syntax("Cannot handle note length for note",q);

	V5 "%s: length %d gives head %d, dots %d, flags %d\n",F,len,head,dots,flags V;

	s->head = head;
	s->dots = dots;
	s->flags = flags;
}


/* ----- double_note: change note length for > or < char --- */
/* Note: if symv[ivc][i] is a chord, the length shifted to the following
	 note is taken from the first note head. Problem: the crazy syntax
	 permits different lengths within a chord. */
void double_note (i,num,sign,q)
int i,num,sign;
char *q;
{
	int m,shift,j,len;

	if ((symv[ivc][i].type != NOTE) && (symv[ivc][i].type != REST))
		bug("sym is not NOTE or REST in double_note", 1);

	shift = 0;
	len = symv[ivc][i].lens[0];
	for (j = 0;j<num;j++) {
		len = len/2;
		shift -= sign*len;
		symv[ivc][i].len += sign*len;
		for (m = 0;m<symv[ivc][i].npitch;m++) symv[ivc][i].lens[m] += sign*len;
	}
	identify_note(&symv[ivc][i],q);
	carryover += shift;
}

/* ----- parse_basic_note: parse note or rest with pitch and length --*/
int parse_basic_note (pitch,length,accidental)
	int *pitch,*length,*accidental;
{	char *F= "parse_basic_note";
	int pit,len,acc;

	acc = pit = 0;					   /* look for accidental sign */
	if (*P == '=') {
		acc = A_NT;
	}
	if (*P == '^') {
		if (*(P+1) == '^') {
			acc = A_DS; P++;
		} elsif (*(P+1) == '/') {
			acc = A_HSH; P++;
		} else {
			acc = A_SH;
		}
	}
	if (*P == '_') {
		if (*(P+1) == '_') {
			acc = A_DF; P++;
		} elsif (*(P+1) == '/') {
			acc = A_HFT; P++;
		} else {
			acc = A_FT;
		}
	}
	V6 "%s: Note '%c' acc=%d.\n",F,*P,acc V;

	if (acc) {
		P++;
		if (!strchr("CDEFGABcdefgab",*P)) {
			syntax("Missing note after accidental", P-1);
			return 0;
		}
	}
	if (!isnote(*P)) {
		syntax("Expecting note", P);
		P++;
		return 0;
	}

	pit = numeric_pitch(*P);			 /* basic pitch */
	V6 "%s: Note '%c' pit=%d.\n",F,*P,pit V;
	P++;

	while (*P == '\'') {				/* eat up following ' chars */
		pit += 7;
		if (lastks) lastks->shup++;
		V6 "%s: Note '%c' pit=%d after \"'\"\n",F,*P,pit V;
		if (lastks) V2 "%s: shdn=%d shup=%d.\n",F,lastks->shdn,lastks->shup V;
		P++;
	}
	while (*P == ',') {				 /* eat up following , chars */
		pit -= 7;
		if (lastks) lastks->shdn++;
		V6 "%s: Note '%c' pit=%d after \",\"\n",F,*P,pit V;
		if (lastks) V2 "%s: shdn=%d shup=%d.\n",F,lastks->shdn,lastks->shup V;
		P++;
	}
	len = prsLen();
	V6 "%s: Note '%c' pit=%d len=%d.\n",F,*P,pit,len V;

//	do_transpose (voice[ivc].key, &pit, &acc);

	*pitch = pit;
	*length = len;
	*accidental = acc;

	V3 "%s: Parsed basic note, length %d/%d=1/%d, pitch %d\n",F,len,BASE,BASE/(len?len:1),pit V;
	return 1;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Parse for one note or rest with all trimmings.
*/
int prsNote()
{	char *F = "prsNote";
	int c,cl,k,i,chord,m,type,rc,sl1,sl2,j;
	int pitch,length,accidental,invis;
	int num, den;	// Length multiplier
	int ngr,pgr[30],agr[30];
	char *q,*q0;
	Sym *sp;

	V6 "%s: \"%s\"\n",F,P V;
	ngr = prsGrSeq(pgr,agr); /* grace notes */
	prsAch();			// permit chord after graces
	prsDeco();			// decorations
	prsAnn();			// permit annotation after graces
	V5 "%s: %d graces %d decos found.\n",F,ngr,decos V;
	prsAnn();			/* permit annotation after decoration */
	prsAch();			/* permit chord after decoration */
	while (isspace(*P)) P++;

	chord = 0;			/* determine if chord */
	q = P;
	if (*P == '[') {	// Standard multi-note chord notation
		V5 "%s: Chord start %8.8s...\n",F,P V;
		chord = 1;
		P++;
	}
	type = invis = 0;
	if (isnote(*P)) type = NOTE;
//	if (chord && (*P == '(')) type = NOTE;	// Why was this here twice?
	if (chord && (*P == ')')) type = NOTE;	/* this just for better error msg */
	if ((*P == 'z')||(*P == 'Z')) {type = REST; }
	if ((*P == 'x')||(*P == 'X')) {type = REST; invis = 1; }	/* Invisible   played rest */
	if ((*P == 'y')||(*P == 'Y')) {type = REST; invis = 1; }	/* Invisible unplayed rest */
	if (!type) return 0;
//	if (!type) {
//		V2 "%s: No note; assuming y\n",F V;
//		type = REST; invis = 1;
//	}
	V5 "%s: Type %d at <%s>\n",F,type,P V;

	k = add_sym(type);		/* add new symbol to list */
	sp = &symv[ivc][k];

	copyDecosToSym (sp);
	sp->gr.n = ngr;			/* Grace note count */
	V5 "%s: Bar symv[%d][%d] has %d decorations and %d grace notes.\n",F,ivc,k,sp->dc.n,sp->gr.n V;
	for (i = 0;i<ngr;i++) {
		sp->gr.p[i] = pgr[i];
		sp->gr.a[i] = agr[i];
	}
	if (strlen(ach)>0) {		/* Copy achord to voice */
		ach_transpose (voice[ivc].key);
		strcpy (sp->ach, ach);
		V3 "%s: symv[%d][%d].ach=\"%s\"\n",F,ivc,k,sp->ach V;
		strcpy (ach, "");
	}
	if (ann && (ann->l > 0)) {		/* Copy annotation to voice */
		V6 "%s: Init annotation for voice %d sym %d.\n",F,ivc,k V;
		TextInit(&sp->ann,64);			/* Make sure the sym has an annotation struct */
		V6 "%s: Copy ann=!%s! to voice %d sym %d.\n",F,TextStr(ann),ivc,k V;
		TextAppend(sp->ann,ann->t);	/* Copy the string */
		sp->ann->y = ann->y;		/* Note its Y position */
		sp->ann->p = ann->p;		/* Note its position */
		V3 "%s: symv[%d][%d].ann=!%s! at %6.3f pos %d.\n",F,ivc,k,TextStr(sp->ann),sp->ann->y,sp->ann->p V;
		V6 "%s: Init annotation for ann.\n",F V;
		TextInit(&ann,64);				/* Forget the global annotation */
	}
	if (vb > 7) {
		int v, s;
		for (v = 0; v < nvoice; v++) {
			for (s = 0; s <= voice[v].nsym; s++) {
				V8 "%s: Voice %d symv %d has ann !%s!\n",F,v,s,TextStr(symv[v][s].ann) V;
			}
		}
	}
	q0 = P;
	if (type == REST) {
		P++;
		sp->lens[0] = prsLen();
		sp->npitch = 1;
		sp->invis = invis;
		V3 "  parsed rest, length %d/%d=1/%d\n",sp->lens[0],BASE,BASE/sp->lens[0] V;
	} else {
		m = 0;								 /* get pitch and length */
		sl1 = sl2 = 0;
		for (;;) {
			V6 "%s: %8.8s ...\n",F,P V;
			if (chord && (*P == '(')) {
				sl1++;
				sp->sl1[m] = sl1;
				P++;
			}
			prsDeco();	/* for extra decorations within chord */
			V5 "%s: %d decos found in chord.\n",F,decos V;
			copyDecosToSym (sp);

			if ((rc = parse_basic_note(&pitch,&length,&accidental)) == 0) {
				voice[ivc].nsym--;
				return 0;
			}
			V6 "%s: voice[%d].key=%X lastks=%X pitadj=%d.\n",F,(uint)ivc,(uint)voice[ivc].key,(uint)lastks,lastks->data.pitadj V;
			V6 "%s: voice[%d].key is %s pitadj=%d\n",F,ivc,dmpKsig(voice[ivc].key),voice[ivc].key->data.pitadj V;
			sp->pitadj  = voice[ivc].key->data.pitadj;
			sp->pits[m] = pitch;
			sp->vpos[m] = pitch + sp->pitadj;
			sp->lens[m] = length;
			sp->accs[m] = accidental;
			sp->ti1[m]  =
			sp->ti2[m]  = 0;
			V6 "%s: symv[%d][%d] m=%d pitadj=%d pits=%d vpos=%d lens=%d accs=%d ti1=%d ti2=%d\n",
				F,ivc,k,m,sp->pitadj,sp->pits[m],sp->vpos[m],
				sp->lens[m],sp->accs[m],sp->ti1[m],sp->ti2[m] V;
			for (j = 0;j<ntinext;j++) {
				if (tinext[j] == sp->vpos[m]) sp->ti2[m] = 1;
			}
			if (chord && (*P == '-')) {
				sp->ti1[m] = 1;
				P++;
			}
			if (chord && (*P == ')')) {
				sl2++;
				sp->sl2[m] = sl2;
				P++;
			}
			if (chord && (*P == '-')) {
				sp->ti1[m] = 1;
				P++;
			}
			m++;

			if (!chord) break;
			if (*P == ']') {	// End of chord
				V5 "%s: Chord end %8.8s...\n",F,P V;
				c = *++P;		// Skip over ] and look for length.
				V5 "%s: Chord next char '%c'\n",F,c V;
				if (isdigit(c) || c=='/') {
					V5 "%s: Chord length %8.8s...\n",F,P V;
				}
				break;
			}
			if (*P == '\0') {
				if (chord) syntax("Chord not closed", q);
				return type;
			}
		}
		ntinext = 0;
		for (j = 0;j<m;j++)
			if (sp->ti1[j]) {
				tinext[ntinext] = sp->vpos[j];
				ntinext++;
			}
		sp->npitch = m;
	}

	for (m = 0; m<sp->npitch; m++) {   /* add carryover from > or < */
		if (sp->lens[m]+carryover <= 0) {
			syntax("> leads to zero or negative note length",q0);
		} else {
			sp->lens[m] += carryover;
			V6 "%s: Symv[%d][%d] lens[%d]=%d\n",F,ivc,k,m,sp->lens[m] V;
		}
	}
	if (((c = *P) && isdigit(c)) || c=='/') {	// Does the chord have a length?
		V5 "%s: Chord length %8.8s...\n",F,P V;
		prsMul(&num,&den);					// Parse the multiplier
		V5 "%s: Chord multiplier %d/%d\n",F,num,den V;
		for (m = 0; m<sp->npitch; m++) {	// Adjust the chord's note lengths
			sp->lens[m] *= num;
			sp->lens[m] /= den;
			V6 "%s: sp->lens[%d]=%d\n",F,m,sp->lens[m] V;
		}
	}
	carryover = 0;
	cl = voice[ivc].key->data.clef;
	V3 "%s: Voice %d sym %d is note with %d decos, ach <%s> ann <%s> clef=%d=%s.\n",F,
		ivc,k,sp->dc.n,sp->ach,TextStr(sp->ann),cl,SymClef(cl) V;
	sp->yadd = 0;
	V3 "%s: Init yadd to %d.\n",F,sp->yadd V;
	switch (voice[ivc].key->data.clef) {
	case ALTO: case ALTOu8: case ALTOd8: sp->yadd = -3; break;
	case BASS: case BASSu8: case BASSd8: sp->yadd = -6; break;
	}
	V3 "%s: Symv[%d][%d] Adjust yadd to %d.\n",F,ivc,k,sp->yadd V;
#if 0
#endif
	identify_note(&symv[ivc][k],q0);
	V3 "%s: symv[%d][%d] returns type %d with annotation !%s!\n",F,ivc,k,type,TextStr(sp->ann) V;
	clearDecos ();
	return type;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Parse a note length, returning a numerator and a denominator.   The  general *
* form  is "num/den", where num and den are integers, and both may be missing. *
* The default value for num and den are both 1.  The '/' may be repeated; each *
* '/'  cuts  the  length  in  half (by doubling den).  The return value is the *
* number of chars consumed, though this isn't actually used.				   *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int prsMul(
	int *nump,	// Numerator
	int *denp)	// Denominator
{	int  c, chars=0;
	int  denv=0, numv=0;
	int  dens=0, nums=0, slashes=0;	// Number of chars in each class
	char*F="prsMul";
	*nump = *denp = 1;	// In case we don't find anything
	while ((c = *P)) {
		V6 "%s: c=%02X='%c'\n",F,c,c V;
		switch (c) {
		case '/':
			++slashes;
			++chars;
			++P;
			V6 "%s: slashes=%d.\n",F,slashes V;
			break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			if (slashes) {
				denv = (10*denv) + (c - '0');
				V6 "%s: denv=%d.\n",F,denv V;
			} else {
				numv = (10*numv) + (c - '0');
				V6 "%s: numv=%d.\n",F,numv V;
			}
			++chars;
			++P;
			break;
		default:
			Done;
		}
	}
done:
	if (numv) {*nump = numv; V6 "%s: *nump=%d.\n",F,*nump V;}
	if (denv) {*denp = denv; V6 "%s: *denp=%d.\n",F,*denp V;}
	while (slashes-- > 1) {
		*denp *= 2;
		V6 "%s: *denp=%d.\n",F,*denp V;
	}
	V6 "%s: Return %d with *nump=%d *denp=%d.\n",F,chars,*nump,*denp V;
	return chars;
}


/* ----- prsSym: parse a symbol and return its type -------- */
int prsSym ()
{	char *F="prsSym";
	int i;

	V5 "%s: Called at <%s>\n",F,P V;
	doMacro();
	if (prsAnn())		  {V4 "%s: Return %d=ANNOT\n",F,ANNOT V;	 return ANNOT;}
	if (prsAch())		  {V4 "%s: Return %d=ACHORD\n",F,ACHORD V;   return ACHORD;}
	if (prsBar())		  {V4 "%s: Return %d=BAR\n",F,BAR V;		 return BAR;}
	if (parse_space())	 {V4 "%s: Return %d=SPACE\n",F,SPACE V;	 return SPACE;}
	if (parse_nl())		{V4 "%s: Return %d=NEWLINE\n",F,NEWLINE V; return NEWLINE;}
	if ((i = parse_esc())) {V4 "%s: Return %d=i (esc)\n",F,i V;	   return i;}
	if ((i = prsNote()))   {V4 "%s: Return %d=i (Note)\n",F,i V;	  return i;}
	if (parse_nl())		{V4 "%s: Return %d=NEWLINE\n",F,NEWLINE V; return NEWLINE;}
	V4 "%s: Return 0.\n",F V;
	return 0;
}

/* ----- add_wd ----- */
char *add_wd(str)
char str[];
{
	char *rp;
	int l;

	l = strlen(str);
	if (l == 0) return 0;
	if (nwpool+l+1>NWPOOL)
		rx ("Overflow while parsing vocals; increase NWPOOL and recompile.","");

	strcpy(wpool+nwpool, str);
	rp = wpool+nwpool;
	nwpool = nwpool+l+1;
	return rp;
}

/* ----- parse_vocals: parse words below a line of music ----- */
/* Use '^' to mark a '-' between syllables - hope nobody needs '^' ! */
int parse_vocals (line)
char line[];
{
	int isym;
	char *c,*c1,*w;
	char word[81];
	char *wordz = word+80;	// Last byte of word [jc]

	if ((line[0] != 'w') || (line[1] != ':')) return 0;
	Q = line;

	isym = nsym0-1;
	c = line+2;
	for (;;) {
		while(*c == ' ') c++;
		if (*c == '\0') break;
		c1 = c;
		if ((*c == '_') || (*c == '*') || (*c == '|') || (*c == '-')) {
			word[0] = *c;
			if (*c == '-') word[0] = '^';
			word[1] = '\0';
			c++;
		} else {
			w = word;
			*w = '\0';
			while ((*c != ' ') && (*c != '\0') && (w < wordz)) {
				if ((*c == '_') || (*c == '*') || (*c == '|')) break;
				if (*c == '-') {
					if (*(c-1) != '\\') break;
					w--;
					*w = '-';
				}
				*w = *c; w++; c++;
			}
			if (*c == '-') { *w = '^' ; w++; c++; }
			*w = '\0';
		}
		/* now word contains a word, possibly with trailing '^',
		*  or one of the special characters * | _ -
		*/
		if (!strcmp(word,"|")) {			   /* skip forward to next bar */
			isym++;
			while ((symv[ivc][isym].type != BAR) && (isym<voice[ivc].nsym)) isym++;
			if (isym >= voice[ivc].nsym)
				{ syntax("Not enough bar lines for |",c1); break; }
		} else {								 /* store word in next note */
			w = word;
			while (*w != '\0') {				   /* replace * and ~ by space */
		/* cd: escaping with backslash possible */
				if ( ((*w == '*') || (*w == '~')) && (*(w-1) != '\\') ) *w = ' ';
				w++;
			}
			isym++;
			while ((symv[ivc][isym].type != NOTE) && (isym<voice[ivc].nsym)) isym++;
			if (isym >= voice[ivc].nsym)
				{ syntax("Too few notes for words",c1); break; }
			symv[ivc][isym].wordp[nwline] = add_wd(word);
		}
		if (*c == '\0') break;
	}
	nwline++;
	return 1;
}

/* ----- prsMusLine: parse a music line into symbols ----- */
int prsMusLine (line)
	char line[];
{	char *F = "prsMusLine";
	int type,num,nbr,n,itype,i;
	char msg[81];
	char *p1,*pmx;

	if (ivc >= nvoice) bug ("Trying to parse undefined voice",1);

	nwline = 0;
	nsym0 = voice[ivc].nsym;

	nbr = 0;
	P = Q = line;		// Positions in music line
	pmx = P+strlen(P);	// End of music line
	clearDecos ();

	while (*P != 0) {	// End at null byte
		if (vb > 6) {
			int v, s;
			for (v = 0; v < nvoice; v++) {
				for (s = 0; s < voice[v].nsym; s++) {
					V8 "%s: Voice %d symv %d has ann !%s!\n",F,v,s,TextStr(symv[v][s].ann) V;
				}
			}
		}
		if (P>pmx) break;				/* emergency exit */
		V6 "%s: Parse \"%s\" <=========\n",F,P V;
		type = prsSym();
		V3 "%s: Parsed sym type %d=%s.\n",F,type,SymName(type) V;
		n = voice[ivc].nsym;
		i = n-1;
		if (symv[ivc][i].ann) {
			V8 "%s: Voice %d sym %d has ann !%s!\n",F,ivc,i,TextStr(symv[ivc][i].ann) V;
		} else {
			V6 "%s: Voice %d sym %d has no annotation.\n",F,ivc,i V;
//			V6 "%s: Init annotation for voice %d sym %d.\n",F,ivc,i V;
//			TextInit(&symv[ivc][i].ann,64);	/* So that messages don't bomb */
		}
		V6 "%s: Voice %d sym %d code (%d,%d) !%s!\n",F,ivc,i,symv[ivc][i].type,symv[ivc][i].u,TextStr(symv[ivc][i].ann) V;
		if (vb > 6) {
			int v, s;
			for (v = 0; v < nvoice; v++) {
				for (s = 0; s < voice[v].nsym; s++) {
					V8 "%s: Voice %d symv %d has ann !%s!\n",F,v,s,TextStr(symv[v][s].ann) V;
				}
			}
		}
		if (type == NEWLINE) {
			if ((n>0) && !cfmt.continueall && !cfmt.barsperstaff) {
				symv[ivc][i].eoln = 1;
				if (word) {
					if (last_note >= 0) symv[ivc][last_note].word_end = 1;
					word = 0;
				}
			}
		}
		if (type == ESCSEQ) {
			if (db>3)
			V3 "%s: Handle escape sequence <%s>\n",F, escseq V;
			itype = info_field(escseq);
			V3 "%s: Info field is type %d.\n",F, itype V;
			handle_inside_field(itype);
		}
		if (type == REST) {
			if (pplet) {				   /* n-plet can start on rest */
				symv[ivc][i].p_plet = pplet;
				symv[ivc][i].q_plet = qplet;
				symv[ivc][i].r_plet = rplet;
				pplet = 0;
			}
			last_note = i;
			p1 = P;
		}

		if (type == NOTE) {
			if (!word) {
				symv[ivc][i].word_st = 1;
				word = 1;
			}
			symv[ivc][i].slur_st += nbr;
			nbr = 0;
			if (voice[ivc].end_slur) symv[ivc][i].slur_end++;
			voice[ivc].end_slur = 0;

			if (pplet) {				   /* start of n-plet */
				symv[ivc][i].p_plet = pplet;
				symv[ivc][i].q_plet = qplet;
				symv[ivc][i].r_plet = rplet;
				pplet = 0;
			}
			last_note = last_real_note = i;
			p1 = P;
		}

		/* for slurs into ending boxes */
		if (type == BAR) {
			if (symv[ivc][i].v == 1) voice[ivc].rem_slur = voice[ivc].end_slur;
			if ((symv[ivc][i].v == 2)||(symv[ivc][i].u == B_RREP)||
					(symv[ivc][i].u == B_DREP)) {
				voice[ivc].end_slur = voice[ivc].rem_slur;
				last_note = -1;
			}
		}

		if (word && ((type == BAR)||(type == SPACE))) {
			if (last_real_note >= 0) symv[ivc][last_real_note].word_end = 1;
			word = 0;
		}

		if (!type) {
			V6 "%s: *P=%02X='%c'\n",F,*P,*P V;
			if (*P == '-') {				  /* a-b tie */
				if (last_note >= 0) symv[ivc][last_note].slur_st++;
				voice[ivc].end_slur = 1;
				P++;
			} elsif (*P == '(') {
				P++;
				if (isdigit(*P)) {
					pplet = *P-'0'; qplet = 0; rplet = pplet;
					P++;
					if (*P == ':') {
						P++;
						if (isdigit(*P)) { qplet = *P-'0';  P++; }
						if (*P == ':') {
							P++;
							if (isdigit(*P)) { rplet = *P-'0';  P++; }
						}
					}
				} else {
					nbr++;
				}
			} elsif (*P == ')') {
				if (last_note>0) {
					symv[ivc][last_note].slur_end++;
				} else {
					if (unexpsym != linenum)
						syntax("Unexpected symbol",P);
					unexpsym = linenum;
				}
				P++;
			} elsif (*P == '>') {
				num = 1;
				P++;
				while (*P == '>') { num++; P++; }
				if (last_note<0)
					syntax("No note before > sign", P);
				else
					double_note (last_note, num, 1, p1);
			} elsif (*P == '<') {
				num = 1;
				P++;
				while (*P == '<') { num++; P++; }
				if (last_note<0)
					syntax("No note before < sign", P);
				else
					double_note (last_note, num, -1, p1);
			} elsif (*P == '*') {	 /* ignore stars for now  */
				V3 "%s: Skip '*'\n",F V;
				P++;
			} elsif (*P == '!') {	 /* ditto for '!' */
				V3 "%s: Skip '!'\n",F V;
				P++;
			} else {
				if (unexpsym != linenum) {
					if (*P != '\0') {
						sprintf (msg, "Unexpected symbol '%c'", *P);
					} else {
						sprintf (msg, "Unexpected end of line");
					}
					syntax(msg, P);
					unexpsym = linenum;
				}
				P++;
			}
		}
	}

	/* maybe set end-of-line marker, if symbols were added */
	n = voice[ivc].nsym;

	if (n > nsym0) {
		symv[ivc][n-1].eoln = 1;
		if (type == CONTINUE)   symv[ivc][n-1].eoln = 0;
		if (cfmt.barsperstaff)  symv[ivc][n-1].eoln = 0;
		if (cfmt.continueall)   symv[ivc][n-1].eoln = 0;
	}

	/* break words at end of line */
	if (word && (symv[ivc][n-1].eoln == 1)) {
		symv[ivc][last_note].word_end = 1;
		word = 0;
	}
	if (vb > 4) {
		int v, s;
		for (v = 0; v < nvoice; v++) {
			for (s = 0; s < voice[v].nsym; s++) {
				V8 "%s: Voice %d symv %d has ann !%s!\n",F,v,s,TextStr(symv[v][s].ann) V;
			}
		}
	}

	return TO_BE_CONTINUED;
}

/* ----- is_selected: check selection for current info fields ---- */
int is_selected (xref_str,npat,pat,select_all,search_field)
int npat,select_all,search_field;
char xref_str[],pat[][STRL1];
{
	int i,j,a,b,m;

	/* true if select_all or if no selectors given */
	if (select_all) return 1;
	if (isblankstr(xref_str) && (npat == 0)) return 1;

	for (i = 0;i<npat;i++) {			 /*patterns */
		if (search_field == S_COMPOSER) {
			for (j = 0;j<info.ncomp;j++) {
				if (!m) m = match(info.comp[j],pat[i]);
			}
		}
		elsif (search_field == S_SOURCE)
			m = match(info.src,pat[i]);
		elsif (search_field == S_RHYTHM)
			m = match(info.rhyth,pat[i]);
		else {
			m = match(info.title,pat[i]);
			if ((!m) && (numtitle >= 2)) m = match(info.title2,pat[i]);
			if ((!m) && (numtitle >= 3)) m = match(info.title3,pat[i]);
			if ((!m) && (numtitle >= 4)) m = match(info.title4,pat[i]);
			if ((!m) && (numtitle >= 5)) m = match(info.title5,pat[i]);
			if ((!m) && (numtitle >= 6)) m = match(info.title6,pat[i]);
		}
		if (m) return 1;
	}

	/* check xref against string of numbers */
	P = xref_str;
	while (*P != 0) {
		parse_space();
		a = prsUInt();
		if (!a) return 0;		  /* can happen if invalid chars in string */
		parse_space();
		if (*P == '-') {
			P++;
			parse_space();
			b = prsUInt();
			if (!b) {
				if (xrefnum >= a) return 1;
			} else {
				for (i = a; i <= b; i++) if (xrefnum == i) return 1;
			}
		} else {
			if (xrefnum == a) return 1;
		}
		if (*P == ',') P++;
	}

	return 0;

}

/* ----- rehash_selectors: split selectors into patterns and xrefs -- */
int rehash_selectors (sel_str, xref_str, pat)
	char sel_str[], xref_str[];
	char pat[][STRL1];
{	char *F = "rehash_selectors";
	char *q;
	char arg[501];
	char *argz = arg+500;	// Last byte of arg [jc]
	int i,npat;

	npat = 0;
	strcpy (xref_str, "");
	q = sel_str;
	i = 0;
	while (1) {
		if ((*q == ' ') || (*q == '\0')) {
			arg[i] = '\0';
			i = 0;
			if (!isblankstr(arg)) {
				if (arg[0] == '-') {			   /* skip any flags */
					;
				} elsif (is_xrefstr(arg)) {
					strcat(xref_str, arg);
					strcat(xref_str, " ");
				} else {						 /* pattern with * or + */
					if ((strchr(arg,'*')) || (strchr(arg,'+'))) {
						strcpy(pat[npat],arg);
					}
					else {					   /* simple pattern */
						strcpy(pat[npat],"*");
						strcat(pat[npat],arg);
						strcat(pat[npat],"*");
					}
					npat++;
				}
			}
		} else {
			if (i <= 500) {
				arg[i] = *q;
				i++;
			} else {
				//
			}
		}
		if (*q == '\0') break;
		q++;
	}
	return npat;
}


/* ----- decomment_line: cut off after % ----- */
void decomment_line (ln)
char ln[];
{
	int i;

	for (i = 0;i<strlen(ln);i++) if (ln[i] == '%') ln[i] = '\0';

}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* get_line: read line, do first operations on it
*/
int get_line (fp,lp)
	FILE *fp;
	Str  *lp;
{	char *F="get_line";
	int l;

	setStr(lp,"",0);
	if (feof(fp)) {
		V3 "%s	EOF on input %08X\n",F,(uint)fp V;
		return 0;
	}
	getLine(lp,fp);
	V6 "Line	%s\n",lp->v V;
	lp->l = l = strlen(lp->v);
	if (l>STRL) {
		if (verbose <= 2) fprintf(stderr,"\n");
		fprintf(stderr,"+++ Line %d too long, truncate from %d to %d chars\n",
						linenum, l, STRL);
		l = STRL-1;
		lp->v[l] = '\0';
	}
	if (is_end_line(lp->v))  {
		return 0;
	}
	if (lp->v[lp->l-1] == '\n') --lp->l;
	lp->v[lp->l] = '\0';
	V3 "%s	%3d \"%s\"\n",F,linenum,lp->v V;
	return 1;
}


/* ----- read_line: returns type of line scanned --- */
int read_line(fp,do_music,lp)
	FILE *fp;
	int   do_music;
	Str  *lp;
{	char *F = "read_line";
	int type,nsym0;

	if (!get_line(fp,lp)) {
		V3 "%s: Return E_O_F=%d.\n",F,E_O_F V;
		return E_O_F;
	}
	V5 "%s	Line=\"%s\"\n",F,lp->v V;
	if (isblankstr(lp->v))		return BLANK;
	if (is_pseudocomment(lp->v))  return PSCOMMENT;
	if (is_comment(lp->v))		return COMMENT;
	decomment_line(lp->v);

	if ((type = info_field(lp->v))) {
		V4 "%s	Line type %d from info_field()\n",F,type V;
		/* skip after history field. Nightmarish syntax, that. */
		if (type != HISTORY) {
			return type;
		} else {
			for (;;) {
				if (!get_line(fp,lp)) return E_O_F;
				if (isblankstr(lp->v)) return BLANK;
				if (is_info_field(lp->v)) break;
				add_text(lp->v, TEXT_H);
			}
			type = info_field(lp->v);
			V5 "%s	Line type %d.\n",F,type V;
			return type;
		}
	}

	if (!do_music) return COMMENT;
	if (parse_vocals(lp->v)) return MWORDS;

	/* now parse a real line of music */
	if (nvoice == 0) ivc = switch_voice(DEFVOICE);
	V3 "%s	Voice %d.\n",F,ivc V;
	nsym0 = voice[ivc].nsym;
	V3 "%s	Voice %d now has %d symbols.\n",F,ivc,nsym0 V;
	type = prsMusLine(lp->v);
	V3 "%s	Parsed music symbols %d-%d for voice %d\n",F, nsym0,voice[ivc].nsym-1,ivc V;
	return type;
}

/* ----- do_index: print index of abc file ------ */
void do_index(fp,xref_str,npat,pat,select_all,search_field)
	FILE *fp;
	int npat,select_all,search_field;
	char xref_str[],pat[][STRL1];
{	char *F = "do_index";
	int type,within_tune;
static Str iline = {0};

	unless (iline.m) {
		unless (iline.v = (char*)malloc(BSIZE)) {
			V1 "%s: ### OUT OF MEMORY (Can't get %d for iline) ###\n",F,BSIZE V;
			return;
		}
		iline.m = BSIZE;
	}
	linenum = 0;
	verbose = vb;
	numtitle = 0;
	write_history = 0;
	within_tune = within_block = do_this_tune = 0;
	reset_info (&default_info);
	info = default_info;

	for (;;) {
		if (!get_line(fp,&iline)) break;
		if (is_comment(iline.v)) continue;
		decomment_line (iline.v);
		type = info_field(iline.v);

		switch (type) {

		case XREF:
			if (within_block)
				fprintf(stderr,"+++ Tune %d not closed properly \n", xrefnum);
			numtitle = 0;
			within_tune = 0;
			within_block = 1;
			ntext = 0;
			break;

		case MACRO:
			V2 "Macros not implemented yet.\n" V;
			break;

		case KEY:
			if (!within_block) {
				V3 "%s: Key not within block.\n",F V;
				break;
			}
			if (!within_tune) {
				V3 "%s: Key not within tune.\n",F V;
				tnum2++;
				if (is_selected (xref_str,npat,pat,select_all,search_field)) {
					fprintf(stderr,"  %-4d %-5s %-4s", xrefnum, info.key, info.meter);
					if	(search_field == S_SOURCE)   fprintf(stderr,"  %-15s", info.src);
					elsif (search_field == S_RHYTHM)   fprintf(stderr,"  %-8s",  info.rhyth);
					elsif (search_field == S_COMPOSER) fprintf(stderr,"  %-15s", info.comp[0]);
					if (numtitle == 6)
						fprintf(stderr,"  %s - %s - %s", info.title,info.title5,info.title6);
					if (numtitle == 5) fprintf(stderr,"  %s - %s", info.title, info.title5);
					if (numtitle == 4) fprintf(stderr,"  %s - %s", info.title, info.title4);
					if (numtitle == 3) fprintf(stderr,"  %s - %s", info.title, info.title3);
					if (numtitle == 2) fprintf(stderr,"  %s - %s", info.title, info.title2);
					if (numtitle == 1) fprintf(stderr,"  %s", info.title);

					if (verbose > 1) fprintf(stderr,"\n");
					tnum1++;
				}
				within_tune = 1;
			} else {
				V3 "%s: Key within tune.\n",F V;
			}
			break;
		}
		if (isblankstr(iline.v)) {
			if (within_block && !within_tune)
				fprintf(stderr,"+++ Header not closed in tune %d\n", xrefnum);
			within_tune = 0;
			within_block = 0;
			info = default_info;
		}
	}
	if (within_block && !within_tune)
		fprintf(stderr,"+++ Header not closed in for tune %d\n", xrefnum);
}

