/*
 * Parsing functions.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2009 Jean-François Moine
 * Adapted from abc2ps, Copyright (C) 1996,1997 Michael Methfessel
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

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

#include "abcparse.h"
#include "abc2ps.h" 

struct STAFF_S staff_tb[MAXSTAFF];	/* staff table */
int nstaff;				/* (0..MAXSTAFF-1) */
struct SYMBOL *tsfirst;			/* first symbol in the time sorted list */

struct VOICE_S voice_tb[MAXVOICE];	/* voice table */
static struct VOICE_S *curvoice;	/* current voice while parsing */
struct VOICE_S *first_voice;		/* first voice */
struct SYSTEM *cursys;			/* current system */
static struct SYSTEM *parsys;		/* current system while parsing */

struct FORMAT dfmt;		/* current global format */
unsigned short *micro_tb;	/* ptr to the microtone table of the tune */

static int lyric_nb;			/* current number of lyric lines */
static struct SYMBOL *lyric_start;	/* 1st note of the line for w: */
static struct SYMBOL *lyric_cont;	/* current symbol when w: continuation */

static int over_time;			/* voice overlay start time */
static int over_mxtime;			/* voice overlay max time */
static short over_bar;			/* voice overlay in a measure */
static short over_voice;		/* main voice in voice overlay */
static int staves_found;		/* time of the last %%staves */
static int abc2win;

static int bar_number;			/* (for %%setbarnb) */

float multicol_start;			/* (for multicol) */
static float multicol_max;
static float lmarg, rmarg;

static void get_clef(struct SYMBOL *s);
static void get_key(struct SYMBOL *s);
static void get_meter(struct SYMBOL *s);
static void get_voice(struct SYMBOL *s);
static void get_note(struct SYMBOL *s);
static struct abcsym *process_pscomment(struct abcsym *as);
static void set_tuplet(struct SYMBOL *s);
static void sym_link(struct SYMBOL *s, int type);

/* -- weight of the symbols -- */
static signed char w_tb[15] = {	/* !! index = symbol type !! */
	0,
	9,	/* 1- note / rest */
	1,	/* 2- space */
	3,	/* 3- bar 3 !! used in sort_all !! */
	2,	/* 4- clef */
	5,	/* 5- timesig */
	4,	/* 6- keysig */
	0,	/* 7- tempo */
	0,	/* 8- staves */
	9,	/* 9- mrest */
	0,	/* 10- part */
	8,	/* 11- grace */
	0,	/* 12- fmtchg */
	7,	/* 13- tuplet */
	6	/* 14- stbrk */
};

/* -- expand a multi-rest into single rests and measure bars -- */
static void mrest_expand(struct SYMBOL *s)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s2, *next;
	int nb, dt;

	nb = s->as.u.bar.len;
	dt = s->dur / nb;

	/* change the multi-rest to a single rest */
	s->type = NOTEREST;
	s->as.type = ABC_T_REST;
	s->as.u.note.nhd = 0;
	s->dur = s->as.u.note.lens[0] = dt;
	s->head = H_FULL;
	s->nflags = -2;

	/* add the bar / rest */
	next = s->next;
	p_voice = &voice_tb[s->voice];
	p_voice->last_sym = s;
	p_voice->time = s->time + dt;
	s2 = s;
	while (--nb > 0) {
		s2 = sym_add(p_voice, BAR);
		s2->as.type = ABC_T_BAR;
		s2->as.u.bar.type = B_SINGLE;
		s2->as.linenum = s->as.linenum;
		s2->as.colnum = s->as.colnum;
		s2 = sym_add(p_voice, NOTEREST);
		s2->as.type = ABC_T_REST;
		s2->as.linenum = s->as.linenum;
		s2->as.colnum = s->as.colnum;
		s2->dur = s2->as.u.note.lens[0] = dt;
		s2->head = H_FULL;
		s2->nflags = -2;
		p_voice->time += dt;
	}
	if ((s2->next = next) != 0)
		next->prev = s2;
}

/* -- sort all symbols by time and vertical sequence -- */
static void sort_all(void)
{
	struct SYSTEM *sy;
	struct SYMBOL *s, *prev;
	struct VOICE_S *p_voice;
	int fl, voice, time, w, wmin, multi, mrest_time;
	int nv, nb, r, sysadv;
	struct SYMBOL *vtb[MAXVOICE];
	signed char vn[MAXVOICE];	/* voice indexed by range */

/*	memset(vtb, 0, sizeof vtb); */
	mrest_time = -1;
	multi = first_voice->next != 0;
	for (p_voice = first_voice; p_voice != 0; p_voice = p_voice->next) {
		vtb[p_voice - voice_tb] = s = p_voice->sym;
		if (multi && s != 0 && s->type == MREST && mrest_time < s->time)
			mrest_time = s->time;
	}

	/* initialize the voice order */
	sy = cursys;
	sysadv = 1;
	prev = 0;
	fl = 1;				/* set start of sequence */
	for (;;) {
		if (sysadv) {
/*fixme: should set multi here*/
			sysadv = 0;
			memset(vn, -1, sizeof vn);
			for (voice = 0; voice < MAXVOICE; voice++) {
				r = sy->voice[voice].range;
				if (r < 0)
					continue;
				vn[r] = voice;
			}
		}

		/* search the min time and symbol weight */
		wmin = time = (unsigned) ~0 >> 1;	/* max int */
		nv = nb = 0;
		for (r = 0; r < MAXVOICE; r++) {
			voice = vn[r];
			if (voice < 0)
				break;
			s = vtb[voice];
			if (s == 0 || s->time > time)
				continue;
			w = w_tb[s->type];
			if (s->time < time) {
				time = s->time;
				wmin = w;
				nb = 0;
			} else if (w < wmin) {
				wmin = w;
				nb = 0;
			}
			if (!(s->sflags & S_SECOND)) {
				nv++;
				if (s->type == BAR)
					nb++;
			}
			if (multi && s->type == MREST)
				mrest_time = time;
		}
		if (wmin > 127)
			break;			/* done */

		/* align the measure bars */
		if (nb != 0 && nb != nv) {	/* if other symbol than bars */
			wmin = (unsigned) ~0 >> 1;
			for (r = 0; r < MAXVOICE; r++) {
				voice = vn[r];
				if (voice < 0)
					break;
				s = vtb[voice];
				if (s == 0 || s->time > time
				    || s->type == BAR)
					continue;
				w = w_tb[s->type];
				if (w < wmin)
					wmin = w;
			}
			if (wmin > 127)
				wmin = w_tb[BAR];
		}

		/* if some multi-rest and many voices, expand */
		if (time == mrest_time) {
			for (r = 0; r < MAXVOICE; r++) {
				voice = vn[r];
				if (voice < 0)
					break;
				s = vtb[voice];
				if (s == 0 || s->time != time)
					continue;
				w = w_tb[s->type];
				if (w != wmin)
					continue;
				if (s->type != MREST) {
					mrest_time = -1; /* some note or rest */
					break;
				}
			}
			if (mrest_time < 0) {
				for (r = 0; r < MAXVOICE; r++) {
					voice = vn[r];
					if (voice < 0)
						break;
					s = vtb[voice];
					if (s != 0 && s->type == MREST)
						mrest_expand(s);
				}
			}
		}

		/* link the vertical sequence */
		for (r = 0; r < MAXVOICE; r++) {
			voice = vn[r];
			if (voice < 0)
				break;
			s = vtb[voice];
			if (s == 0 || s->time != time )
				continue;
			w = w_tb[s->type];
			if (w != wmin)
				continue;
			if (fl) {
				fl = 0;
				s->sflags |= S_SEQST;
			}
			if ((s->ts_prev = prev) != 0) {
				prev->ts_next = s;
				if (s->type == BAR
				    && (s->sflags & S_SECOND)
				    && prev->type != BAR
				    && !(s->as.flags & ABC_F_INVIS))
					error(1, s, "Bad measure bar");
			} else	tsfirst = s;
			prev = s;
			vtb[voice] = s->next;
			if (s->type == STAVES) {
				sy = sy->next;
				sysadv = 1;
			}
		}
		fl = wmin;	/* start a new sequence if some space */
	}

	/* if no bar or format_change at end of tune, add a dummy symbol */
	if (prev != 0 && prev->type != BAR && prev->type != FMTCHG) {
		s = info['T' - 'A'];
		s->type = FMTCHG;
		s->u = -1;
		s->sflags = S_SEQST;
		s->time = prev->time + prev->dur;
		s->next = 0;
		s->ts_next = 0;
		prev->ts_next = s;
		s->ts_prev = prev;
		for (;;) {
			prev->sflags &= ~S_EOLN;
			if (prev->sflags & S_SEQST)
				break;
			prev = prev->ts_prev;
		}
	}
}

/* -- move the symbols with no space to the next sysmbol -- */
static void voice_compress(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s, *s2, *ns;
	int sflags;

	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (p_voice->ignore)
			continue;
		for (s = p_voice->sym; s != 0; s = s->next) {
			if (s->time >= staves_found)
				break;
		}
		ns = 0;
		sflags = 0;
		for ( ; s != 0; s = s->next) {
			switch (s->type) {
			case FMTCHG:
				s2 = s->extra;
				if (s2 != 0) {	/* dummy format */
					if (ns == 0)
						ns = s2;
					if (s->prev != 0) {
						s->prev->next = s2;
						s2->prev = s->prev;
					}
					while (s2->next != 0)
						s2 = s2->next;
					if (s->next == 0) {
						ns = 0;
						break;
					}
					s->next->prev = s2;
					s2->next = s->next;
				}
				/* fall thru */
			case TEMPO:
			case PART:
			case TUPLET:
				if (ns == 0)
					ns = s;
				sflags |= s->sflags;
				continue;
			case MREST:		/* don't shift P: and Q: */
				if (ns == 0)
					continue;
				s2 = (struct SYMBOL *) getarena(sizeof *s);
				memset(s2, 0, sizeof *s2);
				s2->type = SPACE;
				s2->as.u.note.lens[1] = -1;
				s2->as.flags = ABC_F_INVIS;
				s2->voice = s->voice;
				s2->staff = s->staff;
				s2->time = s->time;
				s2->sflags = s->sflags;
				s2->next = s;
				s2->prev = s->prev;
				s2->prev->next = s2;
				s->prev = s2;
				s = s2;
				break;
			}
			if (s->as.flags & ABC_F_GRACE) {
				if (ns == 0)
					ns = s;
				while (!(s->as.flags & ABC_F_GR_END))
					s = s->next;
				s2 = (struct SYMBOL *) getarena(sizeof *s);
				memcpy(s2, s, sizeof *s2);
				s2->as.type = 0;
				s2->type = GRACE;
				s2->dur = 0;
				if ((s2->next = s->next) != 0)
					s2->next->prev = s2;
				else	p_voice->last_sym = s2;
				s2->prev = s;
				s->next = s2;
				s = s2;
			}
			if (ns == 0)
				continue;
			s->extra = ns;
			s->sflags |= (sflags & S_EOLN);
			s->prev->next = 0;
			if ((s->prev = ns->prev) != 0)
				s->prev->next = s;
			else	p_voice->sym = s;
			ns->prev = 0;
			ns = 0;
			sflags = 0;
		}

		/* when symbols with no space at end of tune,
		 * add a dummy format */
		if (ns != 0) {
			s = sym_add(p_voice, FMTCHG);
			s->u = -1;		/* nothing */
			s->extra = ns;
			s->prev->next = 0;	/* unlink */
			if ((s->prev = ns->prev) != 0)
				s->prev->next = s;
			else	p_voice->sym = s;
			ns->prev = 0;
		}
	}
}

/* -- duplicate the voices as required -- */
static void voice_dup(void)
{
	struct VOICE_S *p_voice, *p_voice2;
	struct SYMBOL *s, *s2, *g, *g2;
	int voice;

	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if ((voice = p_voice->clone) < 0)
			continue;
		p_voice->clone = -1;
		p_voice2 = &voice_tb[voice];
		for (s = p_voice->sym; s != 0; s = s->next) {
			if (s->time >= staves_found)
				break;
		}
		for ( ; s != 0; s = s->next) {
			s2 = (struct SYMBOL *) getarena(sizeof *s2);
			memcpy(s2, s, sizeof *s2);
			if (p_voice2->sym != 0) {
				p_voice2->last_sym->next = s2;
				s2->prev = p_voice2->last_sym;
			} else	p_voice2->sym = s2;
			p_voice2->last_sym = s2;
			s2->voice = voice;
			s2->staff = p_voice2->staff;
			if (p_voice2->second)
				s2->sflags |= S_SECOND;
			else	s2->sflags &= ~S_SECOND;
			if (p_voice2->floating)
				s2->sflags |= S_FLOATING;
			else	s2->sflags &= ~S_FLOATING;
			s2->ly = 0;
			if (s2->type != GRACE)
				continue;
			g = s2->extra;
			g2 = (struct SYMBOL *) getarena(sizeof *g2);
			memcpy(g2, g, sizeof *g2);
			s2->extra = g2;
			s2 = g2;
			s2->voice = voice;
			s2->staff = p_voice2->staff;
			for (g = g->next; g != 0; g = g->next) {
				g2 = (struct SYMBOL *) getarena(sizeof *g2);
				memcpy(g2, g, sizeof *g2);
				s2->next = g2;
				g2->prev = s2;
				s2 = g2;
				s2->voice = voice;
				s2->staff = p_voice2->staff;
			}
		}
	}
}

/* -- create a new staff system -- */
static void system_new(void)
{
	struct SYSTEM *new_sy;
	int staff, voice;

	new_sy = (struct SYSTEM *) getarena(sizeof *new_sy);
	if (parsys == 0) {
		memset(new_sy, 0, sizeof *new_sy);
		for (voice = 0; voice < MAXVOICE; voice++) {
			new_sy->voice[voice].range = -1;
			new_sy->voice[voice].clef.line = 2;
			new_sy->voice[voice].clef.stafflines = 5;
			new_sy->voice[voice].clef.staffscale = 1;
		}
		cursys = new_sy;
	} else {
		for (voice = 0; voice < MAXVOICE; voice++) {
			if (parsys->voice[voice].range < 0
			    || parsys->voice[voice].second)
				continue;
			staff = parsys->voice[voice].staff;
			memcpy(&parsys->staff[staff].clef,
				&parsys->voice[voice].clef,
				sizeof parsys->staff[staff].clef);
		}
		memcpy(new_sy, parsys, sizeof *new_sy);
		for (voice = 0; voice < MAXVOICE; voice++) {
			new_sy->voice[voice].range = -1;
			new_sy->voice[voice].second = 0;
		}
		for (staff = 0; staff < MAXSTAFF; staff++)
			new_sy->staff[staff].flags = 0;
		parsys->next = new_sy;
	}
	parsys = new_sy;
}

/* -- set the staves -- */
static void staves_init(void)
{
	struct SYSTEM *sy, *new_sy;
	struct SYMBOL *s, *staves;
	int staff, voice;

	sy = cursys;
	for (voice = 0; voice < MAXVOICE; voice++) {
		if (sy->voice[voice].range < 0
		    || sy->voice[voice].second)
			continue;
		staff = sy->voice[voice].staff;
		memcpy(&sy->staff[staff].clef,
			&sy->voice[voice].clef,
			sizeof (struct clef_s));
		sy->staff[staff].sep = sy->voice[voice].sep;
		sy->staff[staff].maxsep = sy->voice[voice].maxsep;
	}
	staves = 0;
	for (s = tsfirst; s != 0; s = s->ts_next) {
		switch (s->type) {
		case STAVES:
			sy = sy->next;
			for (voice = 0; voice < MAXVOICE; voice++) {
				if (sy->voice[voice].range < 0
				    || sy->voice[voice].second)
					continue;
				staff = sy->voice[voice].staff;
				memcpy(&sy->staff[staff].clef,
					&sy->voice[voice].clef,
					sizeof (struct clef_s));
				sy->staff[staff].sep = sy->voice[voice].sep;
				sy->staff[staff].maxsep = sy->voice[voice].maxsep;
			}
			staves = s;
			continue;
		case CLEF:
			if (s->as.u.clef.type < 0)
				break;
			continue;	/* normal clef change */
		case KEYSIG:
		case TIMESIG:
		case TEMPO:
		case PART:
		case FMTCHG:
			continue;
		default:
			staves = 0;
			continue;
		}

		/* #lines or scale of staff found */
		voice = s->voice;
		if (staves == 0) {
			staves = s;	/* create a new staff system */
			new_sy = (struct SYSTEM *) getarena(sizeof *new_sy);
			memcpy(new_sy, sy, sizeof *new_sy);
			new_sy->next = sy->next;
			sy->next = new_sy;
			sy = new_sy;
			s->type = STAVES;	/* and set the marker */
		} else {		/* remove the CLEF */
			if (s->prev != 0)
				s->prev->next = s->next;
			else	voice_tb[voice].sym = s->next;
			if (s->ts_next != 0) {
				s->ts_next->ts_prev = s->ts_prev;
				if (s->sflags & S_SEQST)
					s->ts_next->sflags |= S_SEQST;
			}
			if (s->ts_prev != 0)
				s->ts_prev->ts_next = s->ts_next;
			else	tsfirst = s->ts_next;
			if (s->next != 0)
				s->next->prev = s->prev;
		}
		staff = sy->voice[voice].staff;
		if (s->as.u.clef.stafflines >= 0)
			sy->voice[voice].clef.stafflines
				= sy->staff[staff].clef.stafflines
				= s->as.u.clef.stafflines;
		if (s->as.u.clef.staffscale != 0)
			sy->voice[voice].clef.staffscale
				= sy->staff[staff].clef.staffscale
				= s->as.u.clef.staffscale;
	}
}

/* -- initialize the voices and staves -- */
/* this routine is called when starting the generation */
static void system_init(void)
{
	voice_compress();
	voice_dup();
	sort_all();		/* define the time / vertical sequences */
	parsys->nstaff = nstaff;	/* save the number of staves */
	staves_init();
}

/* -- generate a piece of tune -- */
static void generate(void)
{
	int voice;

	system_init();
	if (tsfirst == 0)
		return;			/* no symbol */
	output_music();

	/* reset the parser */
	for (voice = 0; voice < MAXVOICE; voice++) {
		voice_tb[voice].sym = voice_tb[voice].last_sym = 0;
		voice_tb[voice].time = 0;
		voice_tb[voice].have_ly = 0;
		voice_tb[voice].staff = cursys->voice[voice].staff;
		voice_tb[voice].second = cursys->voice[voice].second;
	}
	if (staves_found > 0)
		staves_found = 0;
}

/* -- output the music and lyrics after tune -- */
static void gen_ly(int eob)
{
	generate();
	if (info['W' - 'A'] != 0) {
		put_words(info['W' - 'A']);
		info['W' - 'A'] = 0;
	}
	if (eob)
		buffer_eob();
}

/* -- change the accidentals and "\\n" in the guitar chords -- */
static void gchord_adjust(struct SYMBOL *s)
{
	char *p, *q;
	int annot, l;

	s->gcf = cfmt.gcf;
	s->anf = cfmt.anf;
	annot = cfmt.freegchord;
	p = s->as.text;
	if (*p == '\0')
		return;
	if (strchr("^_<>@", *p) != 0) {
		annot = 1;		/* annotation */
		p++;
	}
	q = p;
/*fixme: treat 'dim' as 'o', 'halfdim' as '/o', and 'maj' as a triangle*/
	while (*p != '\0') {
		switch (*p) {
		case '#':
		case 'b':
		case '=':
			if (annot)
				break;
			if (p == q)	/* 1st char or after a slash */
				break;
			switch (*p) {
			case '#':
				*p = '\201';
				break;
			case 'b':
				*p = '\202';
				break;
			default:
/*			case '=': */
				*p = '\203';
				break;
			}
			break;
		case '\\':
			p++;
			switch (*p) {
			case '\0':
				return;
			case 'n':
				p[-1] = ';';
				goto move;
			case '#':
				p[-1] = '\201';
				goto move;
			case 'b':
				p[-1] = '\202';
				goto move;
			case '=':
				p[-1] = '\203';
			move:
				l = strlen(p);
				memmove(p, p + 1, l);
				p--;
				break;
			}
			break;
		case ' ':
			if (p != q)
				break;
			/* fall thru */
		case '/':
			q = p + 1;
			break;
		case '\n':
			if (strchr("^_<>@", p[1]) != 0) {
				annot = 1;
				p++;
			} else	annot = cfmt.freegchord;
			q = p + 1;
			break;
		}
		p++;
	}
}

/* -- parse a lyric (vocal) definition -- */
static char *get_lyric(char *p)
{
	struct SYMBOL *s;
	char word[128], *q;
	int ln;
	struct FONTSPEC *f;

	f = &cfmt.font_tb[cfmt.vof];
	str_font(cfmt.vof);			/* (for tex_str) */

	if ((s = lyric_cont) == 0) {
		if (lyric_nb >= MAXLY)
			return "Too many lyric lines";
		ln = lyric_nb++;
		s = lyric_start;
	} else	{
		lyric_cont = 0;
		ln = lyric_nb - 1;
	}

	/* scan the lyric line */
	while (*p != '\0') {
		while (isspace((unsigned char) *p))
			p++;
		if (*p == '\0')
			break;
		switch (*p) {
		case '|':
			while (s != 0
			       && (s->type != BAR
				   || (s->as.flags & ABC_F_INVIS)))
				s = s->next;
			if (s == 0)
				return "Not enough bar lines for lyric line";
			s = s->next;
			p++;
			continue;
		case '-':
			word[0] = '\x02';
			word[1] = '\0';
			p++;
			break;
		case '_':
			word[0] = '\x03';
			word[1] = '\0';
			p++;
			break;
		case '*':
			word[0] = *p++;
			word[1] = '\0';
			break;
		case '\\':
			if (p[1] == '\0') {
				lyric_cont = s;
				return 0;
			}
			/* fall thru */
		default:
			q = word;
			for (;;) {
				unsigned char c;

				c = *p;
				switch (c) {
				case '\0':
				case ' ':
				case '\t':
				case '_':
				case '*':
				case '|':
					break;
				case '~':
					c = ' ';
					goto addch;
				case '-':
					c = '\x02';
					goto addch;
				case '\\':
					if (p[1] == '\0')
						break;
					switch (p[1]) {
					case '~':
					case '_':
					case '*':
					case '|':
					case '-':
					case ' ':
						c = *++p;
						break;
					}
					/* fall thru */
				default:
				addch:
					if (q < &word[sizeof word - 1])
						*q++ = c;
					p++;
					if (c == '\x02')
						break;
					continue;
				}
				break;
			}
			*q = '\0';
			break;
		}

		/* store word in next note */
		while (s != 0
		       && (s->as.type != ABC_T_NOTE
			   || (s->as.flags & ABC_F_GRACE)))
			s = s->next;
		if (s == 0)
			return "Too many words in lyric line";
		if (word[0] != '*') {
			struct lyl *lyl;
			float w;

			if (s->ly == 0) {
				s->ly = (struct lyrics *) getarena(sizeof (struct lyrics));
				memset(s->ly, 0, sizeof (struct lyrics));
			}
			w = tex_str(word);

			/* handle the font change at start of text */
			q = tex_buf;
			if (*q == '$' && isdigit((unsigned char) q[1])
			    && (unsigned) (q[1] - '0') < FONT_UMAX) {
				int ft;

				ft = q[1] - '0';
				if (ft == 0)
					ft = cfmt.vof;
				f = &cfmt.font_tb[ft];
				str_font(ft);
				q += 2;
			}
			lyl = (struct lyl *) getarena(sizeof *s->ly->lyl[0]
						    + strlen(q));
			s->ly->lyl[ln] = lyl;
			lyl->f = f;
			lyl->w = w;
			strcpy(lyl->t, q);

		}
		s = s->next;
	}
	while (s != 0
	       && (s->as.type != ABC_T_NOTE
		   || (s->as.flags & ABC_F_GRACE)))
		s = s->next;
	if (s != 0)
		return "Not enough words for lyric line";
	return 0;
}

/* -- add a voice in the linked list -- */
static void voice_link(struct VOICE_S *p_voice)
{
	struct VOICE_S *p_voice2;

	p_voice2 = first_voice;
	for (;;) {
		if (p_voice2 == p_voice)
			return;
		if (p_voice2->next == 0)
			break;
		p_voice2 = p_voice2->next;
	}
	p_voice2->next = p_voice;
}

/* -- get a voice overlay -- */
static void get_over(struct SYMBOL *s)
{
	struct VOICE_S *p_voice, *p_voice2, *p_voice3;
	int range, voice, voice2, voice3;
static char tx_wrong_dur[] = "Wrong duration in voice overlay";

	/* treat the end of overlay */
	p_voice = curvoice;
	if (p_voice->ignore)
		return;
	if (s->as.type == ABC_T_BAR
	    || s->as.u.v_over.type == V_OVER_E)  {
		over_bar = 0;
		if (over_time < 0) {
			error(1, s,
			      "Erroneous end of voice overlap");
			return;
		}
		if (p_voice->time != over_mxtime)
			error(1, s, tx_wrong_dur);
		curvoice = &voice_tb[over_voice];
		over_voice = -1;
		over_time = -1;
		return;
	}

	/* treat the full overlay start */
	if (s->as.u.v_over.type == V_OVER_S) {
		over_time = p_voice->time;
		return;
	}

	/* (here is treated a new overlay - '&') */
	/* create the extra voice if not done yet */
	voice2 = s->as.u.v_over.voice;
	p_voice2 = &voice_tb[voice2];
	if (parsys->voice[voice2].range < 0) {
		int clone;

		clone = p_voice->clone >= 0;
		p_voice2->name = "&";
		p_voice2->second = 1;
		parsys->voice[voice2].second = 1;
		p_voice2->scale = p_voice->scale;
		range = parsys->voice[p_voice - voice_tb].range;
		for (voice = 0; voice < MAXVOICE; voice++) {
			if (parsys->voice[voice].range > range)
				parsys->voice[voice].range += clone + 1;
		}
		parsys->voice[voice2].range = range + 1;
		voice_link(p_voice2);
		if (clone) {
			for (voice3 = MAXVOICE; --voice3 >= 0; ) {
				if (parsys->voice[voice3].range < 0)
					break;
			}
			if (voice3 > 0) {
				p_voice3 = &voice_tb[voice3];
				p_voice3->name = p_voice2->name;
				p_voice3->second = 1;
				parsys->voice[voice3].second = 1;
				p_voice3->scale = voice_tb[p_voice->clone].scale;
				parsys->voice[voice3].range = range + 2;
				voice_link(p_voice3);
				p_voice2->clone = voice3;
			} else	error(1, s,
				      "Too many voices for overlay cloning");
		}
	}
	voice = p_voice - voice_tb;
	p_voice2->cstaff = p_voice2->staff = parsys->voice[voice2].staff
			= parsys->voice[voice].staff;
	if ((voice3 = p_voice2->clone) >= 0) {
		p_voice3 = &voice_tb[voice3];
		p_voice3->cstaff = p_voice3->staff
				= parsys->voice[voice3].staff
				= parsys->voice[p_voice->clone].staff;
	}

	if (over_time < 0) {			/* first '&' in a measure */
		int time;

		over_bar = 1;
		over_mxtime = p_voice->time;
		over_voice = p_voice - voice_tb;
		time = p_voice2->time;
		for (s = p_voice->last_sym; /*s != 0*/; s = s->prev) {
			if (s->type == BAR
			    || s->time <= time)	/* (if start of tune) */
				break;
		}
		over_time = s->time;
	} else {
		if (over_voice < 0) {
			over_mxtime = p_voice->time;
			over_voice = p_voice - voice_tb;
		} else if (p_voice->time != over_mxtime)
			error(1, s, tx_wrong_dur);
	}
	p_voice2->time = over_time;
	curvoice = p_voice2;
}

/* -- get staves definition (%%staves) -- */
static void get_staves(struct SYMBOL *s)
{
	struct VOICE_S *p_voice, *p_voice2;
	struct staff_s *p_staff;
	int i, flags, voice, staff, range, dup_voice, maxtime;

	voice_compress();
	voice_dup();

	/* create a new staff system */
	curvoice = p_voice = first_voice;
	maxtime = p_voice->time;
	flags = p_voice->sym != 0;
	for (p_voice = p_voice->next; p_voice; p_voice = p_voice->next) {
		if (p_voice->time > maxtime)
			maxtime = p_voice->time;
		if (p_voice->sym != 0)
			flags = 1;
	}
	for (voice = 0, p_voice = voice_tb;
	     voice < MAXVOICE;
	     voice++, p_voice++) {
		p_voice->second = 0;
		p_voice->floating = 0;
		p_voice->ignore = 0;
		p_voice->time = maxtime;
	}
	if (flags == 0			/* if first %%staves */
	    || (maxtime == 0 && staves_found < 0)) {
		for (voice = 0; voice < MAXVOICE; voice++)
			parsys->voice[voice].range = -1;
	} else {			/* else, have a new staff system */
		parsys->nstaff = nstaff;
		sym_link(s, STAVES);
		s->as.state = ABC_S_HEAD; /* (output PS sequences immediately) */
		system_new();
	}
	staves_found = maxtime;

	/* initialize the voices */
	dup_voice = MAXVOICE;
	range = 0;
	p_staff = s->as.u.staves;
	parsys->top_voice = p_staff->voice;
	for (i = 0;
	     i < MAXVOICE && p_staff->voice >= 0;
	     i++, p_staff++) {
		voice = p_staff->voice;
		p_voice = &voice_tb[voice];
		if (parsys->voice[voice].range >= 0) {
			if (parsys->voice[dup_voice - 1].range >= 0) {
				error(1, s, "Too many voices for cloning");
				continue;
			}
			voice = --dup_voice;	/* duplicate the voice */
			p_voice2 = &voice_tb[voice];
			memcpy(p_voice2, p_voice, sizeof *p_voice2);
			p_voice2->next = 0;
			while (p_voice->clone > 0)
				p_voice = &voice_tb[p_voice->clone];
			p_voice->clone = voice;
			p_voice = p_voice2;
			p_staff->voice = voice;
		}
		parsys->voice[voice].range = range++;
		voice_link(p_voice);
	}

	/* change the behavior from %%staves to %%score */
	if (s->as.text[3] == 't') {		/* if %%staves */
		for (i = 0, p_staff = s->as.u.staves;
		     i < MAXVOICE - 2 && p_staff->voice >= 0;
		     i++, p_staff++) {
			flags = p_staff->flags;
			if (!(flags & (OPEN_BRACE | OPEN_BRACE2)))
				continue;
			if (p_staff[1].flags != 0)
				continue;
			if ((flags & OPEN_PARENTH)
			    || (p_staff[2].flags & OPEN_PARENTH))
				continue;

			/* {a b c} --> {a *b c} */
			if (p_staff[2].flags & (CLOSE_BRACE | CLOSE_BRACE2))
				p_staff[1].flags |= FL_VOICE;

			/* {a b c d} --> {(a b) (c d)} */
			else if (p_staff[2].flags == 0
				 && (p_staff[3].flags & (CLOSE_BRACE | CLOSE_BRACE2))) {
				p_staff->flags |= OPEN_PARENTH;
				p_staff[1].flags |= CLOSE_PARENTH;
				p_staff[2].flags |= OPEN_PARENTH;
				p_staff[3].flags |= CLOSE_PARENTH;
			}
		}
	}

	/* set the staff system */
	staff = -1;
	for (i = 0, p_staff = s->as.u.staves;
	     i < MAXVOICE && p_staff->voice >= 0;
	     i++, p_staff++) {
		flags = p_staff->flags;
		if ((flags & (OPEN_PARENTH | CLOSE_PARENTH))
				== (OPEN_PARENTH | CLOSE_PARENTH)) {
			flags &= ~(OPEN_PARENTH | CLOSE_PARENTH);
			p_staff->flags = flags;
		}
		voice = p_staff->voice;
		p_voice = &voice_tb[voice];
		if (flags & FL_VOICE) {
			p_voice->floating = 1;
			p_voice->second = 1;
		} else {
#if MAXSTAFF < MAXVOICE
			if (staff >= MAXSTAFF - 1) {
				error(1, s, "Too many staves");
			} else
#endif
				staff++;
			parsys->staff[staff].flags = 0;
		}
		p_voice->staff = p_voice->cstaff
			= parsys->voice[voice].staff = staff;
		parsys->staff[staff].flags |= flags;
		if (flags & OPEN_PARENTH) {
			while (i < MAXVOICE) {
				i++;
				p_staff++;
				voice = p_staff->voice;
				p_voice = &voice_tb[voice];
				p_voice->second = 1;
				p_voice->staff = p_voice->cstaff
					= parsys->voice[voice].staff
					= staff;
				if (p_staff->flags & CLOSE_PARENTH)
					break;
			}
			parsys->staff[staff].flags |= p_staff->flags;
		}
	}
	if (staff < 0)
		staff = 0;
	nstaff = staff;
	for (voice = 0; voice < MAXVOICE; voice++)
		parsys->voice[voice].second = voice_tb[voice].second;
}

/* -- re-initialize all potential voices -- */
static void voice_init(void)
{
	struct VOICE_S *p_voice;
	int i;

	for (i = 0, p_voice = voice_tb;
	     i < MAXVOICE;
	     i++, p_voice++) {
		p_voice->sym = p_voice->last_sym = 0;
		p_voice->bar_start = 0;
		p_voice->time = 0;
		p_voice->slur_st = 0;
		p_voice->hy_st = 0;
		p_voice->tie = 0;
		p_voice->rtie = 0;
	}
}

/* -- identify info line, store in proper place	-- */
static char *state_txt[4] = {
	"global", "header", "tune", "embedded"
};
static void get_info(struct SYMBOL *s,
		     int info_type,
		     struct abctune *t)
{
	INFO *inf;
	char *p;
	int old_lvl;

	/* change global or local */
	inf = s->as.state == ABC_S_GLOBAL ? &default_info : &info;
	old_lvl = lvlarena(s->as.state != ABC_S_GLOBAL);

	switch (info_type) {
	case 'd':
		break;
	case 'I':
		process_pscomment(&s->as);	/* same as pseudo-comment */
		break;
	case 'K':
		get_key(s);
		if (s->as.state != ABC_S_HEAD)
			break;
		tunenum++;
		PUT2("%% --- %s (%s) ---\n"
			"%% --- font ",
			&info['X' - 'A']->as.text[2],
			&info['T' - 'A']->as.text[2]);
		outft = -1;
		set_font(TITLEFONT);		/* for index */
		outft = -1;
		PUT0("\n");
		if (!epsf)
			bskip(cfmt.topspace);
		write_heading(t);
		if (!cfmt.printtempo)
			info['Q' - 'A'] = 0;
		nbar = nbar_rep = cfmt.measurefirst;	/* measure numbering */
		over_voice = -1;
		over_time = -1;
		over_bar = 0;
		reset_gen();

		/* switch to the 1st voice */
		curvoice = &voice_tb[parsys->top_voice];
		break;
	case 'L':
		break;
	case 'M':
		get_meter(s);
		break;
	case 'P':
		if (!cfmt.printparts)
			break;
		switch (s->as.state) {
		case ABC_S_GLOBAL:
		case ABC_S_HEAD:
			(*inf)['P' - 'A'] = s;
			break;
		case ABC_S_TUNE: {
			struct VOICE_S *p_voice;

			p_voice = curvoice;
			curvoice = &voice_tb[parsys->top_voice];
			sym_link(s, PART);
			if (p_voice->time != curvoice->time) {
				error(1, s, "Misplaced P:");
				if (p_voice->time > curvoice->time)
					s->time = curvoice->time = p_voice->time;
				else	p_voice->time = curvoice->time;
			}
			curvoice = p_voice;
			break;
		    }
		default:
			sym_link(s, PART);
			break;
		}
		break;
	case 'Q':
		if (curvoice != &voice_tb[parsys->top_voice])
			break;		/* tempo only for first voice */
		switch (s->as.state) {
		case ABC_S_GLOBAL:
		case ABC_S_HEAD:
			(*inf)['Q' - 'A'] = s;
			break;
		default:
			if (cfmt.printtempo)
				sym_link(s, TEMPO);
			break;
		}
		break;
	case 's':
		break;
	case 'T':
		switch (s->as.state) {
		case ABC_S_GLOBAL:	/* T: without X: */
			goto newtune;
		case ABC_S_HEAD:
			goto addinfo;
		default:
			gen_ly(1);
			write_title(s);
			bskip(cfmt.musicspace + 0.2 CM);
			voice_init();
			reset_gen();		/* (display the time signature) */
			curvoice = &voice_tb[parsys->top_voice];
			break;
		}
		break;
	case 'U': {
		unsigned char *deco;

		deco = s->as.state == ABC_S_GLOBAL ? deco_glob : deco_tune;
		deco[s->as.u.user.symbol] = deco_intern(s->as.u.user.value);
		break;
	    }
	case 'u':
		break;
	case 'V':
		get_voice(s);
		break;
	case 'w':
		if (cfmt.musiconly
		    || s->as.state != ABC_S_TUNE)
			break;
		if (lyric_start == 0)
			break;
		p = &s->as.text[2];
		while (isspace((unsigned char) *p))
		       p++;
		if ((p = get_lyric(p)) != 0)
			error(1, s, "%s", p);
		curvoice->have_ly = 1;
		break;
	case 'W':
		if (s->as.state == ABC_S_GLOBAL)
			break;
		goto addinfo;
	case 'X':
newtune:
		if (!epsf)
			write_buffer();	/* flush stuff left from %% lines */
		dfmt = cfmt;		/* save the format at start of tune */
		memcpy(&info, &default_info, sizeof info);
		info['X' - 'A'] = s;
		if (info_type == 'T')
			info['T' - 'A'] = s;
		memcpy(&deco_tune, &deco_glob, sizeof deco_tune);
		lvlarena(1);
		break;
	default:
addinfo:
		if (info_type >= 'A' && info_type <= 'Z') {
			struct SYMBOL *prev;

			prev = (*inf)[info_type - 'A'];
			if (prev == 0
			    || (prev->as.state == ABC_S_GLOBAL
				&& s->as.state != ABC_S_GLOBAL)) {
				(*inf)[info_type - 'A'] = s;
				break;
			}
			while (prev->next != 0)
				prev = prev->next;
			prev->next = s;
			s->prev = prev;
			break;
		}
		error(1, s, "%s info '%c:' not treated",
			state_txt[(int) s->as.state], info_type);
		break;
	}
	lvlarena(old_lvl);
}

/* -- set head type, dots, flags for note -- */
void identify_note(struct SYMBOL *s,
		   int dur,
		   int *p_head,
		   int *p_dots,
		   int *p_flags)
{
	int head, dots, flags;

	if (dur % 12 != 0)
		error(1, s, "Invalid note duration");
	dur /= 12;			/* see BASE_LEN for values */
	if (dur == 0)
		error(1, s, "Note too short");
	for (flags = 5; dur != 0; dur >>= 1, flags--) {
		if (dur & 1)
			break;
	}
	dur >>= 1;
	switch (dur) {
	case 0: dots = 0; break;
	case 1: dots = 1; break;
	case 3: dots = 2; break;
	case 7: dots = 3; break;
	default:
		error(1, s, "Note too much dotted");
		dots = 3;
		break;
	}
	flags -= dots;
	if (flags >= 0)
		head = H_FULL;
	else switch (flags) {
	default:
		error(1, s, "Note too long");
		flags = -4;
		/* fall thru */
	case -4:
		head = H_SQUARE;
		break;
	case -3:
		head = cfmt.squarebreve ? H_SQUARE : H_OVAL;
		break;
	case -2:
		head = H_OVAL;
		break;
	case -1:
		head = H_EMPTY;
		break;
	}
	*p_head = head;
	*p_flags = flags;
	*p_dots = dots;
}

/* -- measure bar -- */
static void get_bar(struct SYMBOL *s)
{
	int bar_type;
	struct SYMBOL *s2, *s3;

	if (curvoice->norepbra && s->as.u.bar.repeat_bar)
		s->sflags |= S_NOREPBRA;

	bar_type = s->as.u.bar.type;
	s3 = 0;
	s2 = curvoice->last_sym;
	if (s2 != 0) {

		/* remove the invisible repeat bars when no shift is needed */
		if (bar_type == B_OBRA
		    && (curvoice == &voice_tb[parsys->top_voice]
			|| (parsys->staff[curvoice->staff - 1].flags & STOP_BAR)
			|| (s->sflags & S_NOREPBRA))) {
			if (s2->type == BAR && s2->as.text == 0) {
				s2->as.text = s->as.text;
				s2->as.u.bar.repeat_bar = s->as.u.bar.repeat_bar;
				s2->sflags |= (s->sflags & S_NOREPBRA);
				return;
			}
		}

		/* merge back-to-back repeat bars */
		if (bar_type == B_LREP && s->as.text == 0) {
			if (s2->type == BAR
			    && s2->as.u.bar.type == B_RREP) {
				s2->as.u.bar.type = B_DREP;
				return;
			}
		}

		/* the bar must be before any key signature */
/*fixme:and time signature??*/
		if ((s2->type == KEYSIG /*|| s2->type == TIMESIG*/)
		    && (s2->prev == 0 || s2->prev->type != BAR))
			s3 = s2;
	}

	/* link the bar in the voice */
	if (s3 != 0) {
		s2 = curvoice->last_sym;
		curvoice->last_sym = s3->prev;
		sym_link(s, BAR);
		s->next = s3;
		s3->prev = s;
		curvoice->last_sym = s2;
	} else	sym_link(s, BAR);
	s->staff = curvoice->staff;	/* original staff */

	/* set some flags */
	switch (bar_type) {
	case B_OBRA:
/*	case B_CBRA:			thick bar or end of repeat braket */
	case (B_OBRA << 4) + B_CBRA:
		s->as.flags |= ABC_F_INVIS;
		break;
	}
	if ((bar_type & 0xf0) != 0) {
		do {
			bar_type >>= 4;
		} while ((bar_type & 0xf0) != 0);
		if (bar_type == B_COL)
			s->sflags |= S_RRBAR;
	}
	if (bar_number != 0
	    && curvoice == &voice_tb[parsys->top_voice]) {
		s->u = bar_number;		/* set the new bar number */
		bar_number = 0;
	}
	if (s->as.u.bar.dc.n > 0)
		deco_cnv(&s->as.u.bar.dc, s, 0); /* convert the decorations */
	if (s->as.text != 0 && !s->as.u.bar.repeat_bar)
		gchord_adjust(s);		/* adjust the guitar chords */
}

/* -- activate the tablature from the command line '-T' -- */
static void set_tblt(struct VOICE_S *p_voice)
{
	struct tblt_s *tblt;
	int i;

	for (i = 0; i < ncmdtblt; i++) {
		if (!cmdtblts[i].active)
			continue;
		if (cmdtblts[i].vn[0] != '\0') {
			if (strcmp(cmdtblts[i].vn, p_voice->name) != 0
			    && (p_voice->nm == 0
				|| strcmp(cmdtblts[i].vn, p_voice->nm) != 0)
			    && (p_voice->snm == 0
				|| strcmp(cmdtblts[i].vn, p_voice->snm) != 0))
				continue;
		}
		tblt = tblts[cmdtblts[i].index];
		if (p_voice->tblts[0] == tblt
		    || p_voice->tblts[1] == tblt)
			continue;
		if (p_voice->tblts[0] == 0)
			p_voice->tblts[0] = tblt;
		else	p_voice->tblts[1] = tblt;
	}
}

/* -- do a tune -- */
void do_tune(struct abctune *t,
	     int header_only)
{
	struct abcsym *as;
	struct SYMBOL *s, *s2;
	int i;

	/* initialize */
	lvlarena(0);
	nstaff = 0;
	staves_found = -1;
	memset(staff_tb, 0, sizeof staff_tb);
	memset(voice_tb, 0, sizeof voice_tb);
	for (i = 0; i < MAXVOICE; i++) {
		voice_tb[i].clef.line = 2;	/* treble clef on 2nd line */
		voice_tb[i].clef.stafflines = 5;
		voice_tb[i].clef.staffscale = 1;
		voice_tb[i].meter.nmeter = 1;
		voice_tb[i].meter.wmeasure = BASE_LEN;
		voice_tb[i].meter.meter[0].top[0] = '4';
		voice_tb[i].meter.meter[0].bot[0] = '4';
		voice_tb[i].wmeasure = BASE_LEN;
		voice_tb[i].scale = 1;
		voice_tb[i].clone = -1;
	}
	curvoice = first_voice = voice_tb;
	voice_tb[0].name = "1";		/* implicit voice */
	set_tblt(first_voice);
	micro_tb = t->micro_tb;		/* microtone values */
	abc2win = 0;

	parsys = 0;
	system_new();			/* create the 1st staff system */
	parsys->top_voice =
		parsys->voice[0].range = 0;	/* implicit voice */

	if (cfmt.oneperpage) {
		use_buffer = 0;
		close_page();
	} else	use_buffer = !cfmt.splittune;

	/* set the duration of all notes/rests - this is needed for tuplets */
	if (!header_only) {
		for (as = t->first_sym; as != 0; as = as->next) {
			switch (as->type) {
			case ABC_T_EOLN:
				if (as->u.eoln.type == 2)
					abc2win = 1;
				break;
			case ABC_T_NOTE:
			case ABC_T_REST:
				s = (struct SYMBOL *) as;
				s->dur = s->as.u.note.lens[0];
				break;
			}
		}
	}

	/* scan the tune */
	for (as = t->first_sym; as != 0; as = as->next) {
		if (header_only && as->state != ABC_S_GLOBAL)
			break;
		s = (struct SYMBOL *) as;
		switch (as->type) {
		case ABC_T_INFO: {
			int info_type;

			switch (as->text[0]) {
			case 'X':
			case 'T':
			case 'W':
				if (header_only)
					continue;
			}
			info_type = as->text[0];
			for (;;) {
				get_info(s, info_type, t);
				if (as->next == 0
				    || as->next->type != ABC_T_INFO2)
					break;
				as = as->next;
				s = (struct SYMBOL *) as;
			}
			break;
		    }
		case ABC_T_PSCOM:
			as = process_pscomment(as);
			break;
		case ABC_T_NOTE:
		case ABC_T_REST:
			if (curvoice->space) {
				curvoice->space = 0;
				s->as.flags |= ABC_F_SPACE;
			}
			get_note(s);
			break;
		case ABC_T_BAR:
			if (over_bar)
				get_over(s);
			get_bar(s);
			break;
		case ABC_T_CLEF:
			get_clef(s);
			break;
		case ABC_T_EOLN:
			if (cfmt.breakoneoln
			    || (s->as.flags & ABC_F_SPACE))
				curvoice->space = 1;
			if (curvoice->second)
				continue;
			if (cfmt.continueall || cfmt.barsperstaff
			    || as->u.eoln.type == 1)	/* if '\' */
				continue;
			if (as->u.eoln.type == 0	/* if normal eoln */
			    && abc2win)
				continue;
			if (curvoice->last_sym != 0)
				curvoice->last_sym->sflags |= S_EOLN;
			if (!cfmt.alignbars)
				continue;
			i = (curvoice - voice_tb) + 1;
			if (i < cfmt.alignbars) {
				curvoice = &voice_tb[i];
				continue;
			}
			gen_ly(1);
			curvoice = &voice_tb[0];
			continue;
		case ABC_T_MREST: {
			int dur;

			dur = curvoice->wmeasure * as->u.bar.len;
			if (curvoice->second) {
				curvoice->time += dur;
				break;
			}
			sym_link(s, MREST);
			s->dur = dur;
			curvoice->time += dur;
			if (s->as.text != 0)		/* adjust the */
				gchord_adjust(s);	/* guitar chords */
			if (s->as.u.bar.dc.n > 0)
				deco_cnv(&s->as.u.bar.dc, s, 0);
			break;
		    }
		case ABC_T_MREP: {
			int n;

			if (as->next == 0 || as->next->type != ABC_T_BAR) {
				error(1, s,
				      "Measure repeat not followed by a bar");
				break;
			}
			if (curvoice->ignore)
				break;
			n = as->u.bar.len;
			if (curvoice->second) {
				curvoice->time += curvoice->wmeasure * n;
				break;
			}
			s2 = sym_add(curvoice, NOTEREST);
			s2->as.type = ABC_T_REST;
			s2->as.linenum = as->linenum;
			s2->as.colnum = as->colnum;
			s2->as.flags |= ABC_F_INVIS;
			s2->dur = curvoice->wmeasure;
			curvoice->time += s2->dur;
			if (n == 1) {
				as->next->u.bar.len = n; /* <n> in the next bar */
				break;
			}
			while (--n > 0) {
				s2 = sym_add(curvoice, BAR);
				s2->as.linenum = as->linenum;
				s2->as.colnum = as->colnum;
				s2->as.u.bar.type = B_SINGLE;
				if (n == as->u.bar.len - 1)
					s2->as.u.bar.len = as->u.bar.len;
				s2 = sym_add(curvoice, NOTEREST);
				s2->as.type = ABC_T_REST;
				s2->as.linenum = as->linenum;
				s2->as.colnum = as->colnum;
				s2->as.flags |= ABC_F_INVIS;
				s2->dur = curvoice->wmeasure;
				curvoice->time += s2->dur;
			}
			break;
		    }
		case ABC_T_V_OVER:
			get_over(s);
			continue;
		case ABC_T_TUPLET:
			set_tuplet(s);
			break;
		default:
			continue;
		}
		if (s->type == 0)
			continue;
		if (curvoice->second)
			s->sflags |= S_SECOND;
		if (curvoice->floating)
			s->sflags |= S_FLOATING;
	}

	gen_ly(0);
	if (!header_only
	    && cfmt.writehistory)
		put_history();
	buffer_eob();
	if (epsf) {
		if (nbuf > 0)
			write_eps();
	} else	write_buffer();

	if (info['X' - 'A'] != 0) {
		cfmt = dfmt;	/* restore the format at start of tune */
		info['X' - 'A'] = 0;
	}
	clrarena(1);
	
}

/* -- get a clef definition (in K: or V:) -- */
static void get_clef(struct SYMBOL *s)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s2;
	int stafflines, voice;
	float staffscale;

	p_voice = curvoice;
	if (s->as.prev->type == ABC_T_INFO) {
		switch (s->as.prev->text[0]) {
		case 'K':
			if (s->as.prev->state != ABC_S_HEAD)
				break;
			if (s->as.u.clef.type >= 0) {
				for (voice = 0; voice < MAXVOICE; voice++) {
					stafflines = parsys->voice[voice].clef.stafflines;
					staffscale = parsys->voice[voice].clef.staffscale;
					memcpy(&parsys->voice[voice].clef, &s->as.u.clef,
					       sizeof parsys->voice[voice].clef);
					parsys->voice[voice].clef.stafflines = stafflines;
					parsys->voice[voice].clef.staffscale = staffscale;
					voice_tb[voice].forced_clef = 1;
				}
			}
			if ((stafflines = s->as.u.clef.stafflines) >= 0) {
				for (voice = 0; voice < MAXVOICE; voice++)
					parsys->voice[voice].clef.stafflines = stafflines;
			}
			if ((staffscale = s->as.u.clef.staffscale) != 0) {
				for (voice = 0; voice < MAXVOICE; voice++)
					parsys->voice[voice].clef.staffscale = staffscale;
			}
			return;
		case 'V':	/* clef relative to a voice definition (in the header) */
			p_voice = &voice_tb[(int) s->as.prev->u.voice.voice];
			break;
		}
	}
	voice = p_voice - voice_tb;

	if (p_voice->last_sym == 0) {		/* first clef */
		if ((stafflines = s->as.u.clef.stafflines) < 0)
			stafflines = parsys->voice[voice].clef.stafflines;
		if ((staffscale = s->as.u.clef.staffscale) == 0)
			staffscale = parsys->voice[voice].clef.staffscale;
		if (s->as.u.clef.type >= 0) {
			memcpy(&parsys->voice[voice].clef,
			       &s->as.u.clef,
			       sizeof parsys->voice[voice].clef);
		}
		parsys->voice[voice].clef.stafflines = stafflines;
		parsys->voice[voice].clef.staffscale = staffscale;
	} else {				/* clef change */
		if (s->as.u.clef.type < 0) {	/* if stafflines or staffscale only */
			sym_link(s, CLEF);	/* (will be changed to STAVES) */
			return;
		}

		/* the clef must appear before a key signature or a bar */
		s2 = curvoice->last_sym;
		if (s2 != 0 && (s2->type == KEYSIG || s2->type == BAR)) {
			struct SYMBOL *s3;

			for (s3 = s2; s3->prev != 0; s3 = s3->prev) {
				switch (s3->prev->type) {
				case KEYSIG:
				case BAR:
					continue;
				}
				break;
			}
			curvoice->last_sym = s3->prev;
			sym_link(s, CLEF);
			s->next = s3;
			s3->prev = s;
			curvoice->last_sym = s2;
		} else	sym_link(s, CLEF);
		s->u = 1;	/* small clef */
		if (s->as.u.clef.stafflines >= 0
		    || s->as.u.clef.staffscale != 0) {
			s2 = sym_add(curvoice, CLEF);
			s2->as.linenum = s->as.linenum;
			s2->as.colnum = s->as.colnum;
			s2->as.u.clef.type = -1;
			s2->as.u.clef.stafflines = s->as.u.clef.stafflines;
			s2->as.u.clef.staffscale = s->as.u.clef.staffscale;
		}
	}
	if (s->as.u.clef.type >= 0)
		p_voice->forced_clef = 1;		/* don't change */
}

/* -- set the accidentals when K: with modified accidentals -- */
static void set_acc(struct SYMBOL *s)
{
	int i, j, nacc;
	char accs[8], pits[8];
	static char sharp_tb[8] = {26, 23, 27, 24, 21, 25, 22};
	static char flat_tb[8] = {22, 25, 21, 24, 20, 23, 26};

	if (s->as.u.key.sf > 0) {
		for (nacc = 0; nacc < s->as.u.key.sf; nacc++) {
			accs[nacc] = A_SH;
			pits[nacc] = sharp_tb[nacc];
		}
	} else {
		for (nacc = 0; nacc < -s->as.u.key.sf; nacc++) {
			accs[nacc] = A_FT;
			pits[nacc] = flat_tb[nacc];
		}
	}
	for (i = 0; i < s->as.u.key.nacc; i++) {
		for (j = 0; j < nacc; j++) {
			if ((pits[j] - s->as.u.key.pits[i]) % 7 == 0) {
				accs[j] = s->as.u.key.accs[i];
				break;
			}
		}
		if (j == nacc) {
			accs[j] = s->as.u.key.accs[i];
			pits[j] = s->as.u.key.pits[i];
			nacc++;		/* cannot overflow */
		}
	}
	for (i = 0; i < nacc; i++) {
		s->as.u.key.accs[i] = accs[i];
		s->as.u.key.pits[i] = pits[i];
	}
	s->as.u.key.nacc = nacc;
}

/* -- get a key signature definition (K:) -- */
static void get_key(struct SYMBOL *s)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s2;
	int i;

	if (s->as.u.key.empty == 1)
		return;				/* clef only */
	if (s->as.u.key.sf != 0
	    && !s->as.u.key.exp
	    && s->as.u.key.nacc != 0)
		set_acc(s);
	switch (s->as.state) {
	case ABC_S_HEAD:
		for (i = MAXVOICE, p_voice = voice_tb;
		     --i >= 0;
		     p_voice++) {
			memcpy(&p_voice->key, &s->as.u.key, sizeof p_voice->key);
			p_voice->sfp = s->as.u.key.sf;
			if (p_voice->key.bagpipe
			    && p_voice->stem == 0)
				p_voice->stem = -1;
		}
		break;
	case ABC_S_TUNE:
	case ABC_S_EMBED:
		if (curvoice->last_sym == 0) {

			/* if first symbol of the first voice, apply to all voices */
			if (curvoice == &voice_tb[parsys->top_voice]
			    && s->as.state == ABC_S_TUNE) {	/* (not embedded) */
				for (i = MAXVOICE, p_voice = voice_tb;
				     --i >= 0;
				     p_voice++) {
					memcpy(&p_voice->key, &s->as.u.key,
					       sizeof p_voice->key);
					p_voice->sfp = s->as.u.key.sf;
					if (p_voice->key.bagpipe
					    && p_voice->stem == 0)
						p_voice->stem = -1;
				}
			} else {
				memcpy(&curvoice->key, &s->as.u.key,
				       sizeof curvoice->key);
				curvoice->sfp = s->as.u.key.sf;
				if (curvoice->key.bagpipe
				    && curvoice->stem == 0)
					curvoice->stem = -1;
			}
			break;
		}
		if (curvoice->sfp == s->as.u.key.sf	/* if same key */
		    && s->as.next->type != ABC_T_CLEF)	/* but not explicit clef */
			break;				/* ignore */

		/* the key signature must appear before a time signature */
		s2 = curvoice->last_sym;
		if (s2 != 0 && s2->type == TIMESIG) {
			curvoice->last_sym = s2->prev;
			sym_link(s, KEYSIG);
			s->next = s2;
			s2->prev = s;
			curvoice->last_sym = s2;
		} else	sym_link(s, KEYSIG);
		s->u = curvoice->sfp;		/* old key signature */
		curvoice->sfp = s->as.u.key.sf;
	}
}

/* -- set meter from M: -- */
static void get_meter(struct SYMBOL *s)
{
	struct VOICE_S *p_voice;
	int i;

	switch (s->as.state) {
	case ABC_S_GLOBAL:
		/*fixme: keep the values and apply to all tunes?? */
		break;
	case ABC_S_HEAD: {
		for (i = MAXVOICE, p_voice = voice_tb;
		     --i >= 0;
		     p_voice++) {
			memcpy(&p_voice->meter, &s->as.u.meter,
			       sizeof p_voice->meter);
			p_voice->wmeasure = s->as.u.meter.wmeasure;
		}
		break;
	    }
	case ABC_S_TUNE:
	case ABC_S_EMBED:
		curvoice->wmeasure = s->as.u.meter.wmeasure;
		if (curvoice->last_sym == 0) {

			/* if first symbol of the first voice, apply to all voices */
			if (curvoice == &voice_tb[parsys->top_voice]
			    && s->as.state == ABC_S_TUNE) {	/* (not embedded) */
				for (i = MAXVOICE, p_voice = voice_tb;
				     --i >= 0;
				     p_voice++) {
					memcpy(&p_voice->meter, &s->as.u.meter,
					       sizeof p_voice->meter);
					p_voice->wmeasure = s->as.u.meter.wmeasure;
				}
			} else	memcpy(&curvoice->meter, &s->as.u.meter,
				       sizeof curvoice->meter);
			reset_gen();	/* (display the time signature) */
			break;
		}
		if (s->as.u.meter.nmeter == 0)
			break;		/* M:none */
		sym_link(s, TIMESIG);
		break;
	}
}

/* -- treat a 'V:' -- */
static void get_voice(struct SYMBOL *s)
{
	struct VOICE_S *p_voice;
	int voice;

	voice = s->as.u.voice.voice;
	p_voice = &voice_tb[voice];
	if (parsys->voice[voice].range < 0) {
		if (cfmt.alignbars) {
			error(1, s, "V: does not work with %%%%alignbars");
		}
		if (staves_found < 0) {
			if (!s->as.u.voice.merge) {
#if MAXSTAFF < MAXVOICE
				if (nstaff >= MAXSTAFF - 1) {
					error(1, s, "Too many staves");
					return;
				}
#endif
				nstaff++;
			} else {
				p_voice->second = 1;
				parsys->voice[voice].second = 1;
			}
			p_voice->staff = p_voice->cstaff = nstaff;
			parsys->voice[voice].staff = nstaff;
			{
				int range, i;

				range = 0;
				for (i = 0; i < MAXVOICE; i++) {
					if (parsys->voice[i].range > range)
						range = parsys->voice[i].range;
				}
				parsys->voice[voice].range = range + 1;
				voice_link(p_voice);
			}
		} else {
			p_voice->ignore = 1;
			p_voice->staff = p_voice->cstaff = nstaff + 1;
		}
	}

	/* if something has changed, update */
	if (s->as.u.voice.name != 0)
		p_voice->name = s->as.u.voice.name;
	if (s->as.u.voice.fname != 0) {
		p_voice->nm = s->as.u.voice.fname;
		p_voice->new_name = 1;
	}
	if (s->as.u.voice.nname != 0)
		p_voice->snm = s->as.u.voice.nname;
	if (s->as.u.voice.stem != 0)
		p_voice->stem = s->as.u.voice.stem != 2
			? s->as.u.voice.stem : 0;
	if (s->as.u.voice.gstem != 0)
		p_voice->gstem = s->as.u.voice.gstem != 2
			? s->as.u.voice.gstem : 0;
	if (s->as.u.voice.dyn != 0)
		p_voice->dyn = s->as.u.voice.dyn != 2
			? s->as.u.voice.dyn : 0;
	if (s->as.u.voice.lyrics != 0)
		p_voice->ly_pos = s->as.u.voice.lyrics != 2
			? s->as.u.voice.lyrics : 0;
	if (s->as.u.voice.gchord != 0)
		p_voice->gchord = s->as.u.voice.gchord != 2
			? s->as.u.voice.gchord : 0;
	if (s->as.u.voice.scale != 0)
		p_voice->scale = s->as.u.voice.scale;

	set_tblt(p_voice);

	/* if in tune, switch to this voice */
	switch (s->as.state) {
	case ABC_S_TUNE:
	case ABC_S_EMBED:
		curvoice = p_voice;
		break;
	}
}

/* -- note or rest -- */
static void get_note(struct SYMBOL *s)
{
	struct SYMBOL *prev;
	int i, m;

	prev = curvoice->last_sym;
	s->nhd = m = s->as.u.note.nhd;
	if (!(s->as.flags & ABC_F_GRACE))
		s->stem = curvoice->stem;
	else {			/* grace note - adjust its duration */
		int div;

		s->stem = curvoice->gstem;
		if (!curvoice->key.bagpipe) {
			div = 4;
			if (curvoice->last_sym == 0
			    || !(curvoice->last_sym->as.flags & ABC_F_GRACE)) {
				if (s->as.flags & ABC_F_GR_END)
					div = 2;	/* one grace note */
			}
		} else	div = 8;
		for (i = 0; i <= m; i++)
			s->as.u.note.lens[i] /= div;
		s->dur /= div;
	}
	sym_link(s,  s->as.u.note.lens[0] != 0 ? NOTEREST : SPACE);
	if (!(s->as.flags & ABC_F_GRACE))
		curvoice->time += s->dur;
	s->nohdix = -1;

	/* convert the decorations */
	if (s->as.u.note.dc.n > 0)
		deco_cnv(&s->as.u.note.dc, s, prev);

	/* change the figure of whole measure rests */
	if (s->as.type == ABC_T_REST) {
		if (s->dur == curvoice->wmeasure) {
			if (s->dur < BASE_LEN * 2)
				s->as.u.note.lens[0] = BASE_LEN;
			else if (s->dur < BASE_LEN * 4)
				s->as.u.note.lens[0] = BASE_LEN * 2;
			else	s->as.u.note.lens[0] = BASE_LEN * 4;
		}
	}

	/* sort by pitch the notes of the chord (lowest first) */
	else for (;;) {
		int nx = 0;

		for (i = 1; i <= m; i++) {
			if (s->as.u.note.pits[i] < s->as.u.note.pits[i-1]) {
				int k;
#define xch(f) \
	k = s->as.u.note.f[i]; \
	s->as.u.note.f[i] = s->as.u.note.f[i-1]; \
	s->as.u.note.f[i-1] = k
				xch(pits);
				xch(lens);
				xch(accs);
				xch(sl1);
				xch(sl2);
				xch(ti1);
				xch(decs);
#undef xch
				nx++;
			}
		}
		if (nx == 0)
			break;
	}

	memcpy(s->pits, s->as.u.note.pits, sizeof s->pits);

	/* get the max head type, number of dots and number of flags */
	{
		int head, dots, nflags, l;

		if ((l = s->as.u.note.lens[0]) != 0) {
			identify_note(s, l, &head, &dots, &nflags);
			s->head = head;
			s->dots = dots;
			s->nflags = nflags;
			for (i = 1; i <= m; i++) {
				if (s->as.u.note.lens[i] == l)
					continue;
				identify_note(s, s->as.u.note.lens[i],
					      &head, &dots, &nflags);
				if (head > s->head)
					s->head = head;
				if (dots > s->dots)
					s->dots = dots;
				if (nflags > s->nflags)
					s->nflags = nflags;
			}
			if (s->sflags & S_XSTEM)
				s->nflags = 0;		/* word start+end */
		}
	}
	if (s->nflags <= -2)
		s->as.flags |= ABC_F_STEMLESS;

	if (s->sflags & (S_TREM1 | S_TREM2)) {
		if (s->nflags > 0)
			s->nflags += s->u;
		else
			s->nflags = s->u;
		if ((s->sflags & S_TREM2) && (s->sflags & S_BEAM_END)) {
			prev->head = s->head;
			prev->u = s->u;
			prev->nflags = s->nflags;
			prev->as.flags |= (s->as.flags & ABC_F_STEMLESS);
		}
	}


	for (i = 0; i <= m; i++) {
		if (s->as.u.note.sl1[i] != 0)
			s->sflags |= S_SL1;
		if (s->as.u.note.sl2[i] != 0)
			s->sflags |= S_SL2;
		if (s->as.u.note.ti1[i] != 0)
			s->sflags |= S_TI1;
	}

	if (s->as.flags & ABC_F_LYRIC_START) {
		lyric_start = s;
		lyric_cont = 0;
		lyric_nb = 0;
	}

	/* adjust the guitar chords */
	if (s->as.text != 0)
		gchord_adjust(s);
}

/* -- treat a postscript definition -- */
static void ps_def(struct SYMBOL *s,
		   char *p)
{
	if (s->as.prev != 0)
		s->as.state = s->as.prev->state;
	if (s->as.state == ABC_S_TUNE
	    || s->as.state == ABC_S_EMBED) {
		sym_link(s, FMTCHG);
		s->u = PSSEQ;
		s->as.text = p;
		s->as.flags |= ABC_F_INVIS;
		if (s->prev != 0 && (s->prev->sflags & S_EOLN)) {
			s->sflags |= S_EOLN;
			s->prev->sflags &= ~S_EOLN;
		}
		return;
	}
	if (file_initialized) {
		PUT1("%s\n", p);
		return;
	}
	interpret_fmt_line("postscript", p, 0);
}

/* -- process a pseudo-comment (%% or I:) -- */
static struct abcsym *process_pscomment(struct abcsym *as)
{
	char w[32], *p;
	float h1;
	struct SYMBOL *s = (struct SYMBOL *) as;

	p = as->text + 2;		/* skip '%%' */
	while (isspace((unsigned char) *p))
		p++;
	if (strncasecmp(p, "fmt ", 4) == 0) {
		p += 4;			/* skip 'fmt' */
		while (isspace((unsigned char) *p))
			p++;
	}
	p = get_str(w, p, sizeof w);
	switch (w[0]) {
	case 'b':
		if (strcmp(w, "beginps") == 0) {
			for (;;) {
				if (as->next == 0)
					return as;
				as = as->next;
				p = as->text;
				if (*p == '%' && p[1] == '%') {
					p += 2;
					while (isspace((unsigned char) *p))
						p++;
					if (strncasecmp(p, "fmt ", 4) == 0) {
						p += 4;
						while (isspace((unsigned char) *p))
							p++;
					}
					if (strncmp(p, "endps", 5) == 0)
						return as;
				}
				if (*p == '%')
					continue;	/* skip comment lines */
				s = (struct SYMBOL *) as;
				ps_def(s, p);
			}
			/* not reached */
		}
		if (strcmp(w, "begintext") == 0) {
			int job;

			if (epsf && as->state != ABC_S_HEAD)
				return as;
			if ((job = cfmt.textoption) == T_SKIP)
				return as;
			if (*p != '\0') {
				job = get_textopt(p);
				if (job < 0) {
					error(1, s,
					      "Bad argument for begintext: %s", p);
					job = T_LEFT;
				}
			}
			gen_ly(1);
			for (;;) {
				if (as->next == 0)
					return as;
				as = as->next;
				p = as->text;
				if (*p == '%' && p[1] == '%') {
					p += 2;
					while (isspace((unsigned char) *p))
						p++;
					if (strncasecmp(p, "fmt ", 4) == 0) {
						p += 4;
						while (isspace((unsigned char) *p))
							p++;
					}
					if (strncmp(p, "endtext", 7) == 0) {
						if (job != T_SKIP)
							write_text_block(job, as->state);
						return as;
					}
				}
				if (job != T_SKIP)
					add_to_text_block(p, job);
			}
			/* not reached */
		}
		break;
	case 'E':
		if (strcmp(w, "EPS") == 0) {
			float x1, y1, x2, y2;
			FILE *fp;
			char fn[BSIZE], line[BSIZE];

			gen_ly(1);
			if (cfmt.textoption == T_SKIP)
				return as;
			get_str(line, p, BSIZE);
			if ((fp = open_file(line, "eps", fn)) == 0) {
				error(1, s, "No such file: %s", line);
				return as;
			}

			/* get the bounding box */
			while (fgets(line, sizeof line, fp)) {
				if (strncmp(line, "%%BoundingBox:", 14) == 0) {
					if (sscanf(&line[14], "%f %f %f %f",
						   &x1, &y1, &x2, &y2) == 4)
						break;
				}
			}
			fclose(fp);
			if (strncmp(line, "%%BoundingBox:", 14) != 0) {
				error(1, s,
				      "No bounding box in '%s'", fn);
				return as;
			}
			if (cfmt.textoption == T_CENTER
			    || cfmt.textoption == T_RIGHT) {
				float lw;

				lw = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
					- cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale;
				if (cfmt.textoption == T_CENTER)
					x1 += (lw - (x2 - x1)) * 0.5;
				else	x1 += lw - (x2 - x1);
			}
			PUT0("\001");	/* include file (must be the first after eob) */
			bskip(y2 - y1);
			PUT3("%.2f %.2f%%%s\n", x1, y1, fn);
			buffer_eob();
			return as;
		}
		break;
	case 'm':
		if (strcmp(w, "maxsysstaffsep") == 0) {
			if (as->state != ABC_S_TUNE
			    && as->state != ABC_S_EMBED)
				break;
			parsys->voice[curvoice - voice_tb].maxsep = scan_u(p);
			return as;
		}
		if (strcmp(w, "measrep") == 0)
			goto irepeat;
		if (strcmp(w, "multicol") == 0) {
			float bposy;

			generate();
			buffer_eob();
			if (strncmp(p, "start", 5) == 0) {
				if (!in_page)
					PUT0("%%\n");	/* initialize the output */
				bposy = get_bposy();
				multicol_max = multicol_start = bposy;
				lmarg = cfmt.leftmargin;
				rmarg = cfmt.rightmargin;
			} else if (strncmp(p, "new", 3) == 0) {
				if (multicol_start == 0)
					error(1, s,
					      "%%%%multicol new without start");
				else {
					bposy = get_bposy();
					if (bposy < multicol_start)
						abskip(bposy - multicol_start);
					if (bposy < multicol_max)
						multicol_max = bposy;
					cfmt.leftmargin = lmarg;
					cfmt.rightmargin = rmarg;
				}
			} else if (strncmp(p, "end", 3) == 0) {
				if (multicol_start == 0)
					error(1, s,
					      "%%%%multicol end without start");
				else {
					bposy = get_bposy();
					if (bposy > multicol_max)
						abskip(bposy - multicol_max);
					cfmt.leftmargin = lmarg;
					cfmt.rightmargin = rmarg;
					multicol_start = 0;
					PUT0("%%\n");	/* force write_buffer */
					buffer_eob();
				}
			} else {
				error(1, s,
				      "Unknown keyword '%s' in %%%%multicol", p);
			}
			return as;
		}
		break;
	case 'n':
		if (strcmp(w, "newpage") == 0) {
			if (epsf)
				return as;
			gen_ly(0);
			write_buffer();
			use_buffer = 0;
			if (isdigit((unsigned char) *p))
				pagenum = atoi(p);
			close_page();
			return as;
		}
		break;
	case 'p':
		if (strcmp(w, "postscript") == 0) {
			ps_def(s, p);
			return as;
		}
		break;
	case 'r':
		if (strcmp(w, "repbra") == 0) {
			if (as->state != ABC_S_TUNE
			    && as->state != ABC_S_EMBED)
				return as;
			curvoice->norepbra = !atoi(p);
			return as;
		}
		if (strcmp(w, "repeat") == 0) {
			int n, k;

irepeat:
			if (curvoice->last_sym == 0) {
				error(1, s,
				      "%%%%repeat cannot start a tune");
				return as;
			}
			if (*p == '\0') {
				n = 1;
				k = 1;
			} else {
				n = atoi(p);
				if (n < 1
				    || (curvoice->last_sym->type == BAR
					&& n > 2)) {
					error(1, s,
					      "Incorrect 1st value in %%%%repeat");
					return as;
				}
				while (*p != '\0' && !isspace((unsigned char) *p))
					p++;
				while (isspace((unsigned char) *p))
					p++;
				if (*p == '\0')
					k = 1;
				else {
					k = atoi(p);
					if (k < 1
					    || (curvoice->last_sym->type == BAR
						&& n == 2
						&& k > 1)) {
						error(1, s,
						      "Incorrect 2nd value in %%%%repeat");
						return as;
					}
				}
			}
			s->u = REPEAT;
			if (curvoice->last_sym->type == BAR)
				s->doty = n;
			else	s->doty = -n;
			sym_link(s, FMTCHG);
			s->nohdix = k;
			as->text = 0;
			return as;
		}
		break;
	case 's':
		if (strcmp(w, "setbarnb") == 0) {
			if (as->state == ABC_S_TUNE
			    || as->state == ABC_S_EMBED) {
				bar_number = atoi(p);
				return as;
			}
			strcpy(w, "measurefirst");
			break;
		}
		if (strcmp(w, "sep") == 0) {
			float h2, len, lwidth;

			gen_ly(0);
			lwidth = (cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
				- cfmt.leftmargin - cfmt.rightmargin;
			h1 = h2 = len = 0;
			if (*p != '\0') {
				h1 = scan_u(p);
				while (*p != '\0' && !isspace((unsigned char) *p))
					p++;
				while (isspace((unsigned char) *p))
					p++;
			}
			if (*p != '\0') {
				h2 = scan_u(p);
				while (*p != '\0' && !isspace((unsigned char) *p))
					p++;
				while (isspace((unsigned char) *p))
					p++;
			}
			if (*p != '\0')
				len = scan_u(p);
			if (h1 < 1)
				h1 = 0.5 CM;
			if (h2 < 1)
				h2 = h1;
			if (len < 1)
				len = 3.0 CM;
			bskip(h1);
			PUT2("%.1f %.1f sep0\n",
			     len / cfmt.scale,
			     (lwidth - len) * 0.5 / cfmt.scale);
			bskip(h2);
			buffer_eob();
			return as;
		}
		if (strcmp(w, "staff") == 0) {
			int staff;

			if (as->state != ABC_S_TUNE
			    && as->state != ABC_S_EMBED)
				return as;
			if (*p == '+')
				staff = curvoice->cstaff + atoi(p + 1);
			else if (*p == '-')
				staff = curvoice->cstaff - atoi(p + 1);
			else	staff = atoi(p) - 1;
			if ((unsigned) staff > nstaff) {
				error(1, s, "Bad staff in %%%%staff");
				return as;
			}
			curvoice->floating = 0;
			curvoice->cstaff = staff;
			return as;
		}
		if (strcmp(w, "staffbreak") == 0) {
			if (as->state != ABC_S_TUNE
			    && as->state != ABC_S_EMBED)
				return as;
			sym_link(s, STBRK);
			if (isdigit(*p)) {
				s->xmx = scan_u(p);
				if (p[strlen(p) - 1] == 'f')
					s->doty = 1;
			} else {
				s->xmx = 0.5 CM;
				if (*p == 'f')
					s->doty = 1;
			}
			return as;
		}
		if (strcmp(w, "staves") == 0
		    || strcmp(w, "score") == 0) {
			if (as->state == ABC_S_GLOBAL)
				return as;
#if 0
			if (as->state == ABC_S_TUNE
			    || as->state == ABC_S_EMBED) {
				generate();
				buffer_eob();
				voice_init();
			}
#endif
			get_staves(s);
			return as;
		}
		if (strcmp(w, "sysstaffsep") == 0) {
			if (as->state != ABC_S_TUNE
			    && as->state != ABC_S_EMBED)
				break;
			parsys->voice[curvoice - voice_tb].sep = scan_u(p);
			return as;
		}
		break;
	case 'c':
	case 't':
		if (strcmp(w, "text") == 0 || strcmp(w, "center") == 0) {
			int job;

			if (epsf && as->state == ABC_S_GLOBAL)
				return as;
			if ((job = cfmt.textoption) == T_SKIP)
				return as;
			if (w[0] == 'c')
				job = T_CENTER;
			gen_ly(1);
			add_to_text_block(p, job);
			write_text_block(job, as->state);
			return as;
		}
		if (strcmp(w, "tablature") == 0) {
			struct tblt_s *tblt;
			int i, j;

			tblt = tblt_parse(p);
			if (tblt == 0)
				return as;

			switch (as->state) {
			case ABC_S_TUNE:
			case ABC_S_EMBED:
				for (i = 0; i < ncmdtblt; i++) {
					if (cmdtblts[i].active)
						continue;
					j = cmdtblts[i].index;
					if (j < 0 || tblts[j] == tblt)
						return as;
				}
				/* !! 2 tblts per voice !! */
				if (curvoice->tblts[0] == tblt
				    || curvoice->tblts[1] == tblt)
					break;
				if (curvoice->tblts[1] != 0) {
					error(1, s, "Too many tablatures for voice %s",
						curvoice->name);
					break;
				}
				if (curvoice->tblts[0] == 0)
					curvoice->tblts[0] = tblt;
				else	curvoice->tblts[1] = tblt;
				break;
			}
			return as;
		}
		break;
	case 'v':
		if (strcmp(w, "vskip") == 0) {
			gen_ly(0);
			h1 = scan_u(p);
			bskip(h1);
			buffer_eob();
			return as;
		}
		break;
	}
	if (as->state == ABC_S_TUNE
	    || as->state == ABC_S_EMBED) {
		if (strcmp(w, "leftmargin") == 0
		    || strcmp(w, "rightmargin") == 0
		    || strcmp(w, "scale") == 0) {
			gen_ly(1);
		}
	}
	interpret_fmt_line(w, p, 0);
	if (cfmt.alignbars && strcmp(w, "alignbars") == 0) {
		int i;

		generate();
		if ((unsigned) cfmt.alignbars > MAXSTAFF) {
			error(1, s, "Too big value in %%%%alignbars");
			cfmt.alignbars = MAXSTAFF;
		}
		if (staves_found >= 0)		/* (compatibility) */
			cfmt.alignbars = nstaff + 1;
		first_voice = curvoice = &voice_tb[0];
		for (i = 0; i < cfmt.alignbars; i++) {
			voice_tb[i].staff = voice_tb[i].cstaff = i;
			voice_tb[i].next = &voice_tb[i + 1];
			parsys->staff[i].flags |= STOP_BAR;
			parsys->voice[i].staff = i;
			parsys->voice[i].range = i;
		}
		voice_tb[i - 1].next = 0;
		nstaff = i;
	}
	return as;
}

/* -- set the duration of notes/rests in a tuplet -- */
/*fixme: KO if voice change*/
/*fixme: one nesting level only*/
/*fixme: KO if in a grace sequence*/
static void set_tuplet(struct SYMBOL *t)
{
	struct abcsym *as;
	struct SYMBOL *s;
	int l, r, lplet, grace;

	r = t->as.u.tuplet.r_plet;
	grace = t->as.flags & ABC_F_GRACE;

	l = 0;
	for (as = t->as.next; as != 0; as = as->next) {
		if (as->type == ABC_T_TUPLET) {
			struct abcsym *as2;
			int l2, r2;

			r2 = as->u.tuplet.r_plet;
			l2 = 0;
			for (as2 = as->next; as2 != 0; as2 = as2->next) {
				if (as2->type != ABC_T_NOTE
				    && as2->type != ABC_T_REST)
					continue;
				if (as2->u.note.lens[0] == 0)
					continue;
				if (grace ^ (as2->flags & ABC_F_GRACE))
					continue;
				s = (struct SYMBOL *) as2;
				l2 += s->dur;
				if (--r2 <= 0)
					break;
			}
			l2 = l2 * as->u.tuplet.q_plet
					/ as->u.tuplet.p_plet;
			((struct SYMBOL *) as)->u = l2;
			l += l2;
			r -= as->u.tuplet.r_plet;
			if (r == 0)
				break;
			if (r < 0) {
				error(1, t,
				      "Bad nested tuplet");
				break;
			}
			as = as2;
			continue;
		}
		if (as->type != ABC_T_NOTE && as->type != ABC_T_REST)
			continue;
		if (as->u.note.lens[0] == 0)	/* space ('y') */
			continue;
		if (grace ^ (as->flags & ABC_F_GRACE))
			continue;
		s = (struct SYMBOL *) as;
		l += s->dur;
		if (--r <= 0)
			break;
	}
	if (as == 0) {
		error(1, t,
		      "End of tune found inside a tuplet");
		return;
	}
	if (t->u != 0)		/* if nested tuplet */
		lplet = t->u;
	else	lplet = (l * t->as.u.tuplet.q_plet) / t->as.u.tuplet.p_plet;
	r = t->as.u.tuplet.r_plet;
	for (as = t->as.next; as != 0; as = as->next) {
		int olddur;

		if (as->type == ABC_T_TUPLET) {
			int r2;

			r2 = as->u.tuplet.r_plet;
			s = (struct SYMBOL *) as;
			olddur = s->u;
			s->u = (olddur * lplet) / l;
			l -= olddur;
			lplet -= s->u;
			r -= r2;
			for (;;) {
				as = as->next;
				if (as->type != ABC_T_NOTE
				    && as->type != ABC_T_REST)
					continue;
				if (as->u.note.lens[0] == 0)
					continue;
				if (grace ^ (as->flags & ABC_F_GRACE))
					continue;
				if (--r2 <= 0)
					break;
			}
			if (r <= 0)
				goto done;
			continue;
		}
		if (as->type != ABC_T_NOTE && as->type != ABC_T_REST)
			continue;
		if (as->u.note.lens[0] == 0)
			continue;
		if (grace ^ (as->flags & ABC_F_GRACE))
			continue;
		s = (struct SYMBOL *) as;
		s->sflags |= S_IN_TUPLET;
		olddur = s->dur;
		s->dur = (olddur * lplet) / l;
		if (--r <= 0)
			break;
		l -= olddur;
		lplet -= s->dur;
	}
done:
	if (grace)
		error(1, t,
		      "Tuplets in grace note sequence not yet treated");
	else {
		sym_link(t, TUPLET);
		t->u = cfmt.tuplets;
	}
}

/* -- add a new symbol in a voice -- */
struct SYMBOL *sym_add(struct VOICE_S *p_voice, int type)
{
	struct SYMBOL *s;
	struct VOICE_S *p_voice2;

	s = (struct SYMBOL *) getarena(sizeof *s);
	memset(s, 0, sizeof *s);
	p_voice2 = curvoice;
	curvoice = p_voice;
	sym_link(s, type);
	if (p_voice->second)
		s->sflags |= S_SECOND;
	if (p_voice->floating)
		s->sflags |= S_FLOATING;
	curvoice = p_voice2;
	return s;
}

/* -- link a ABC symbol into a voice -- */
static void sym_link(struct SYMBOL *s, int type)
{
	struct VOICE_S *p_voice = curvoice;

/*	memset((&s->as) + 1, 0, sizeof (struct SYMBOL) - sizeof (struct abcsym)); */
	if (!p_voice->ignore) {
		if (p_voice->sym != 0) {
			p_voice->last_sym->next = s;
			s->prev = p_voice->last_sym;
		} else	p_voice->sym = s;
	}
	p_voice->last_sym = s;

	s->type = type;
	s->voice = p_voice - voice_tb;
	s->staff = p_voice->cstaff;
	s->time = p_voice->time;
}
