Commit message: fix: array overflow when wrong duration in voice overlay

Issue #83,
CVE: CVE-2021-32436
CWE: CWE-703
Vulnerability description: An out-of-bounds read in the function write_title() in subs.c of abcm2ps v8.14.11 allows remote attackers to cause a Denial of Service (DoS) via unspecified vectors.

Project: abcm2ps
File: parse.c

static void gch_capo(struct SYMBOL *s)
{
	char *p = s->text, *q, *r;
	int i, l, li = 0;
	static const char *capo_txt = "  (capo: %d)";
	static signed char cap_trans[] =
		{0, 5, -2, 3, -4, 1, -6, -1, 4, -3, 2, -5};

	// search the chord symbols
	for (;;) {
		if (!strchr("^_<>@", *p))
			break;
		p = strchr(p, '\n');
		if (!p)
			return;
		p++;
	}

	// add a capo chord symbol
	i = p - s->text;
	q = strchr(p + 1, '\n');
	if (q)
		l = q - p;
	else
		l = strlen(p);
	if (!capo) {
		capo = 1;
		li = strlen(capo_txt);
	}
	r = (char *) getarena(strlen(s->text) + l + li + 1);
	i += l;
	strncpy(r, s->text, i);		// annotations + chord symbol
	r[i++] = '\n';
	strncpy(r + i, p, l);		// capo
	if (li) {
		sprintf(r + i + l, capo_txt, cfmt.capo);
		l += li;
	}
	if (q)
		strcpy(r + i + l, q);	// ending annotations
	s->text = r;
	gch_tr1(s, i, cap_trans[cfmt.capo % 12]);
}

Project: abcm2ps
File: parse.c

static void sort_all(void)
{
	struct SYSTEM *sy;
	struct SYMBOL *s, *prev, *s2;
	struct VOICE_S *p_voice;
	int fl, voice, time, w, wmin, multi, mrest_time;
	int nb, r, set_sy, new_sy;	// nv
	struct SYMBOL *vtb[MAXVOICE];
	signed char vn[MAXVOICE];	/* voice indexed by range */

/*	memset(vtb, 0, sizeof vtb); */
	mrest_time = -1;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next)
		vtb[p_voice - voice_tb] = p_voice->sym;

	/* initialize the voice order */
	sy = cursys;
	set_sy = 1;
	new_sy = 0;
	prev = NULL;
	fl = 1;				/* (have gcc happy) */
	multi = -1;			/* (have gcc happy) */
	for (;;) {
		if (set_sy) {
		    fl = 1;			// start a new sequence
//		    if (!new_sy) {
		    if (1) {
			set_sy = 0;
			multi = -1;
			memset(vn, -1, sizeof vn);
			for (p_voice = first_voice;
			     p_voice;
			     p_voice = p_voice->next) {
				voice = p_voice - voice_tb;
				r = sy->voice[voice].range;
				if (r < 0)
					continue;
				vn[r] = voice;
				multi++;
			}
		    }
		}

		/* 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 || 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 0
			if (!(s->sflags & S_SECOND)) {
				nv++;
				if (s->type == BAR)
					nb++;
			}
#endif
			if (s->type == MREST) {
				if (s->u.bar.len == 1)
					mrest_expand(s);
				else if (multi > 0)
					mrest_time = time;
			}
		}
		if (wmin > 127)
			break;					/* done */

#if 0
		/* 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 || s->time > time
				 || s->type == BAR)
					continue;
				w = w_tb[s->type];
				if (w < wmin)
					wmin = w;
			}
			if (wmin > 127)
				wmin = w_tb[BAR];
		}
#endif

		/* if some multi-rest and many voices, expand */
		if (time == mrest_time) {
			nb = 0;
			for (r = 0; r < MAXVOICE; r++) {
				voice = vn[r];
				if (voice < 0)
					break;
				s = vtb[voice];
				if (!s || 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 (nb == 0) {
					nb = s->u.bar.len;
				} else if (nb != s->u.bar.len) {
					mrest_time = -1; /* different duration */
					break;
				}
			}
			if (mrest_time < 0) {
				for (r = 0; r < MAXVOICE; r++) {
					voice = vn[r];
					if (voice < 0)
						break;
					s = vtb[voice];
					if (s && 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 || s->time != time
			 || w_tb[s->type] != wmin)
				continue;
			if (s->type == STAVES) {	// change STAVES to a flag
				sy = sy->next;
				set_sy = new_sy = 1;
				if (s->prev)
					s->prev->next = s->next;
				else
					voice_tb[voice].sym = s->next;
				if (s->next)
					s->next->prev = s->prev;
			} else {
				if (fl) {
					fl = 0;
					s->sflags |= S_SEQST;
				}
				if (new_sy) {
					new_sy = 0;
					s->sflags |= S_NEW_SY;
				}
				s->ts_prev = prev;
				if (prev) {
					prev->ts_next = s;
//fixme: bad error when the 1st voice is second
//					if (s->type == BAR
//					 && (s->sflags & S_SECOND)
//					 && prev->type != BAR
//					 && !(s->flags & ABC_F_INVIS))
//						error(1, s, "Bad measure bar");
				} else {
					tsfirst = s;
				}
				prev = s;
			}
			vtb[voice] = s->next;
		}
		fl = wmin;		/* start a new sequence if some space */
	}

	if (!prev)
		return;

	/* if no bar or format_change at end of tune, add a dummy symbol */
	if ((prev->type != BAR && prev->type != FMTCHG)
	 || new_sy) {
		p_voice = &voice_tb[prev->voice];
		p_voice->last_sym = prev;
		s = sym_add(p_voice, FMTCHG);
		s->aux = -1;
		s->time = prev->time + prev->dur;
		s->sflags = S_SEQST;
		if (new_sy)
			s->sflags |= S_NEW_SY;
		prev->ts_next = s;
		s->ts_prev = prev;
		for (;;) {
			prev->sflags &= ~S_EOLN;
			if (prev->sflags & S_SEQST)
				break;
			prev = prev->ts_prev;
		}
	}

	/* if Q: from tune header, put it at start of the music */
	s2 = info['Q' - 'A'];
	if (!s2)
		return;
	info['Q' - 'A'] = NULL;
	s = tsfirst->extra;
	while (s) {
		if (s->type == TEMPO)
			return;			/* already a tempo */
		s = s->next;
	}
	s = tsfirst;
	s2->type = TEMPO;
	s2->voice = s->voice;
	s2->staff = s->staff;
	s2->time = s->time;
	if (s->extra) {
		s2->next = s->extra;
		s2->next->prev = s2;
	}
	s->extra = s2;
}

Project: abcm2ps
File: parse.c

void do_tune(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s, *s1, *s2;
	int i;

	/* initialize */
	lvlarena(0);
	nstaff = 0;
	staves_found = -1;
	for (i = 0; i < MAXVOICE; i++) {
		p_voice = &voice_tb[i];
		s1 = (struct SYMBOL *) getarena(sizeof *s1);
		memset(s1, 0, sizeof *s1);
		s1->type = CLEF;
		s1->voice = i;
		if (cfmt.autoclef) {
			s1->u.clef.type = AUTOCLEF;
			s1->sflags = S_CLEF_AUTO;
		} else {
			s1->u.clef.type = TREBLE;
		}
		s1->u.clef.line = 2;		/* treble clef on 2nd line */
		p_voice->s_clef = s1;
		p_voice->meter.wmeasure = 1;	// M:none
		p_voice->wmeasure = 1;
		p_voice->scale = 1;
		p_voice->clone = -1;
		p_voice->over = -1;
		p_voice->posit = cfmt.posit;
		p_voice->stafflines = NULL;
//		p_voice->staffscale = 0;
	}
	curvoice = first_voice = voice_tb;
	reset_deco();
	abc2win = 0;
	clip_start.bar = -1;
	clip_end.bar = (short unsigned) ~0 >> 1;

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

	if (!epsf) {
//fixme: 8.6.2
#if 1
// fixme: should already be 0
		use_buffer = 0;
#else
		if (cfmt.oneperpage) {
			use_buffer = 0;
			close_page();
		} else {
			if (in_page)		// ??
				use_buffer = cfmt.splittune != 1;
		}
#endif
	} else {
		use_buffer = 1;
		marg_init();
	}

	/* set the duration of all notes/rests
	 *	(this is needed for tuplets and the feathered beams) */
	for (s = parse.first_sym; s; s = s->abc_next) {
		switch (s->abc_type) {
		case ABC_T_EOLN:
			if (s->u.eoln.type == 2)
				abc2win = 1;
			break;
		case ABC_T_NOTE:
		case ABC_T_REST:
			s->dur = s->u.note.notes[0].len;
			break;
		}
	}

	if (voice_tb[0].id[0] == '\0') {	/* single voice */
		voice_tb[0].id[0] = '1';	/* implicit V:1 */
		voice_tb[0].id[1] = '\0';
	}

	/* scan the tune */
	for (s = parse.first_sym; s; s = s->abc_next) {
		if (s->flags & ABC_F_LYRIC_START)
			curvoice->lyric_start = curvoice->last_sym;
		switch (s->abc_type) {
		case ABC_T_INFO:
			s = get_info(s);
			break;
		case ABC_T_PSCOM:
			s = process_pscomment(s);
			break;
		case ABC_T_NOTE:
		case ABC_T_REST:
			if (curvoice->space
			 && !(s->flags & ABC_F_GRACE)) {
				curvoice->space = 0;
				s->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->flags & ABC_F_SPACE))
				curvoice->space = 1;
			if (cfmt.continueall || cfmt.barsperstaff
			 || s->u.eoln.type == 1)	/* if '\' */
				continue;
			if (s->u.eoln.type == 0		/* if normal eoln */
			 && abc2win
			 && parse.abc_vers != (2 << 16))
				continue;
			if (parsys->voice[curvoice - voice_tb].range == 0
			 && curvoice->last_sym)
				curvoice->last_sym->sflags |= S_EOLN;
			if (!cfmt.alignbars)
				continue;		/* normal */

			/* align bars */
			while (s->abc_next) {		/* treat the lyrics */
				if (s->abc_next->abc_type != ABC_T_INFO)
					break;
				switch (s->abc_next->text[0]) {
				case 'w':
					s = get_info(s->abc_next);
					continue;
				case 'd':
				case 's':
					s = s->abc_next;
					continue;
				}
				break;
			}
			i = (curvoice - voice_tb) + 1;
			if (i < cfmt.alignbars) {
				curvoice = &voice_tb[i];
				continue;
			}
			generate();
			buffer_eob(0);
			curvoice = &voice_tb[0];
			continue;
		case ABC_T_MREST: {
			int dur;

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

			s2 = curvoice->last_sym;
			if (!s2 || s2->type != BAR) {
				error(1, s,
				      "No bar before measure repeat");
				break;
			}
			if (curvoice->ignore)
				break;
			n = s->u.bar.len;
			if (curvoice->second) {
				curvoice->time += curvoice->wmeasure * n;
				break;
			}
			s2 = sym_add(curvoice, NOTEREST);
			s2->abc_type = ABC_T_REST;
			s2->flags |= ABC_F_INVIS;
			s2->dur = curvoice->wmeasure;
			curvoice->time += s2->dur;
			if (n == 1) {
				s->abc_next->u.bar.len = n; /* <n> in the next bar */
				break;
			}
			while (--n > 0) {
				s2 = sym_add(curvoice, BAR);
				s2->u.bar.type = B_SINGLE;
				if (n == s->u.bar.len - 1)
					s2->u.bar.len = s->u.bar.len;
				s2 = sym_add(curvoice, NOTEREST);
				s2->abc_type = ABC_T_REST;
				s2->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);
	put_history();
	buffer_eob(1);
	if (epsf) {
		write_eps();
	} else {
		write_buffer();
//		if (!cfmt.oneperpage && in_page)
//			use_buffer = cfmt.splittune != 1;
	}

	if (info['X' - 'A']) {
		memcpy(&cfmt, &dfmt, sizeof cfmt); /* restore global values */
		memcpy(&info, &info_glob, sizeof info);
		memcpy(deco, deco_glob, sizeof deco);
		maps = maps_glob;
		info['X' - 'A'] = NULL;
	}

	/* free the parsing resources */
	{
		struct brk_s *brk, *brk2;

		brk = brks;
		while (brk) {
			brk2 = brk->next;
			free(brk);
			brk = brk2;
		}
		brks = brk;		/* (NULL) */
	}
}

Project: abcm2ps
File: parse.c

static void gch_tr1(struct SYMBOL *s, int i, int i2)
{
	char *p = &s->text[i],
		*q = p + 1,
		*new_txt;
	int l, latin;
	int n, a, i1, i3, i4;
	static const char note_names[] = "CDEFGAB";
	static const char *latin_names[7] =
			{ "Do", "Ré", "Mi", "Fa", "Sol", "La", "Si" };
	static const char *acc_name[5] = {"bb", "b", "", "#", "##"};

	/* main chord */
	latin = 0;
	switch (*p) {
	case 'A':
	case 'B':
		n = *p - 'A' + 5;
		break;
	case 'C':
	case 'E':
	case 'G':
		n = *p - 'C';
		break;
	case 'D':
		if (p[1] == 'o') {
			latin++;
			n = 0;		/* Do */
			break;
		}
		n = 1;
		break;
	case 'F':
		if (p[1] == 'a')
			latin++;	/* Fa */
		n = 3;
		break;
	case 'L':
		latin++;		/* La */
		n = 5;
		break;
	case 'M':
		latin++;		/* Mi */
		n = 2;
		break;
	case 'R':
		latin++;
		if (p[1] != 'e')
			latin++;	/* Ré */
		n = 1;			/* Re */
		break;
	case 'S':
		latin++;
		if (p[1] == 'o') {
			latin++;
			n = 4;		/* Sol */
		} else {
			n = 6;		/* Si */
		}
		break;
	case '/':			// bass only
		latin--;
		break;
	default:
		return;
	}
	q += latin;

	/* allocate a new string */
	new_txt = getarena(strlen(s->text) + 6);
	l = p - s->text;
	memcpy(new_txt, s->text, l);
	s->text = new_txt;
	new_txt += l;
	p = q;

	if (latin >= 0) {			// if some chord
		a = 0;
		while (*p == '#') {
			a++;
			p++;
		}
		while (*p == 'b') {
			a--;
			p++;
		}
//		if (*p == '=')
//			p++;
		i3 = cde2fcg[n] + i2 + a * 7;
		i4 = cgd2cde[(unsigned) ((i3 + 16 * 7) % 7)];
		i1 = ((i3 + 1 + 21) / 7 + 2 - 3 + 32 * 5) % 5;
							/* accidental */
		if (latin == 0)
			*new_txt++ = note_names[i4];
		else
			new_txt += sprintf(new_txt, "%s", latin_names[i4]);
		new_txt += sprintf(new_txt, "%s", acc_name[i1]);
	}

	/* bass */
	while (*p != '\0' && *p != '\n' && *p != '/')	// skip 'm'/'dim'..
		*new_txt++ = *p++;
	if (*p == '/') {
		*new_txt++ = *p++;
//fixme: latin names not treated
		q = strchr(note_names, *p);
		if (q) {
			p++;
			n = q - note_names;
			if (*p == '#') {
				a = 1;
				p++;
			} else if (*p == 'b') {
				a = -1;
				p++;
			} else {
				a = 0;
			}
			i3 = cde2fcg[n] + i2 + a * 7;
			i4 = cgd2cde[(unsigned) ((i3 + 16 * 7) % 7)];
			i1 = ((i3 + 1 + 21) / 7 + 2 - 3 + 32 * 5) % 5;
			*new_txt++ = note_names[i4];
			new_txt += sprintf(new_txt, "%s", acc_name[i1]);
		}
	}
	strcpy(new_txt, p);
}

Project: abcm2ps
File: parse.c

static void parse_defs(char *p, char *q)
{
	char *id, *r;
	int idsz;

	for (;;) {
		id = strstr(p, "id=\"");
		if (!id || id > q)
			return;
		r = strchr(id + 4, '"');
		if (!r)
			return;
		idsz = r + 1 - id;

		// if SVG output, mark the id as defined
		if (svg || epsf > 1) {
			svg_def_id(id, idsz);
			p = r;
			continue;
		}

		// convert SVG to PS
		p = id;
		while (*p != '<')
			p--;
		if (strncmp(p, "<path ", 6) == 0) {
			r = strstr(p, "/>");
			parse_path(p + 6, r, id, idsz);
			if (!r)
				break;
			p = r + 2;
			continue;
		}
		break;
	}
}

Project: abcm2ps
File: parse.c

static void gch_build(struct SYMBOL *s)
{
	struct gch *gch;
	char *p, *q, antype, sep;
	float w, h_ann, h_gch, y_above, y_below, y_left, y_right;
	float xspc;
	int l, ix, box, gch_place;

	if (s->posit.gch == SL_HIDDEN)
		return;
	s->gch = getarena(sizeof *s->gch * MAXGCH);
	memset(s->gch, 0, sizeof *s->gch * MAXGCH);

	if (curvoice->transpose != 0)
		gch_transpose(s);
	if (cfmt.capo)
		gch_capo(s);

	/* split the guitar chords / annotations
	 * and initialize their vertical offsets */
	gch_place = s->posit.gch == SL_BELOW ? -1 : 1;
	h_gch = cfmt.font_tb[cfmt.gcf].size;
	h_ann = cfmt.font_tb[cfmt.anf].size;
	y_above = y_below = y_left = y_right = 0;
	box = cfmt.gchordbox;
	p = s->text;
	gch = s->gch;
	sep = '\n';
	antype = 'g';			/* (compiler warning) */
	for (;;) {
		if (sep != 'n' && strchr("^_<>@", *p)) {
			gch->font = cfmt.anf;
			antype = *p++;
			if (antype == '@') {
				int n;
				float xo, yo;

				if (sscanf(p, "%f,%f%n", &xo, &yo, &n) != 2) {
					error(1, s, "Error in annotation \"@\"");
				} else {
					p += n;
					if (*p == ' ')
						p++;
					gch->x = xo;
					gch->y = yo;
				}
			}
		} else if (sep == '\n') {
			gch->font = cfmt.gcf;
			gch->box = box;
			antype = 'g';
		} else {
			gch->font = (gch - 1)->font;
			gch->box = (gch - 1)->box;
		}
		gch->type = antype;
		switch (antype) {
		default:				/* guitar chord */
			if (gch_place < 0)
				break;			/* below */
			y_above += h_gch;
			if (box)
				y_above += 2;
			break;
		case '^':				/* above */
			y_above += h_ann;
			break;
		case '_':				/* below */
			break;
		case '<':				/* left */
			y_left += h_ann * 0.5;
			break;
		case '>':				/* right */
			y_right += h_ann * 0.5;
			break;
		case '@':				/* absolute */
			if (gch->x == 0 && gch->y == 0
			 && gch != s->gch
			 && s->gch->type == '@') {	/* if not 1st line */
				gch->x = (gch - 1)->x;
				gch->y = (gch - 1)->y - h_ann;
			}
			break;
		}
		gch->idx = p - s->text;
		for (;;) {
			switch (*p) {
			default:
				p++;
				continue;
			case '\\':
				p++;
				if (*p == 'n') {
					p[-1] = '\0';
					break;		/* sep = 'n' */
				}
				p++;
				continue;
			case '&':			/* skip "&xxx;" */
				for (;;) {
					switch (*p) {
					default:
						p++;
						continue;
					case ';':
						p++;
					case '\0':
					case '\n':
					case '\\':
						break;
					}
					break;
				}
				continue;
			case '\0':
			case ';':
			case '\n':
				break;
			}
			break;
		}
		sep = *p;
		if (sep == '\0')
			break;
		*p++ = '\0';
		gch++;
		if (gch - s->gch >= MAXGCH) {
			error(1, s, "Too many guitar chords / annotations");
			break;
		}
	}

	/* change the accidentals in the guitar chords */
	for (ix = 0, gch = s->gch; ix < MAXGCH; ix++, gch++) {
		if (gch->type == '\0')
			break;
		if (gch->type != 'g')
			continue;
		p = s->text + gch->idx;
		q = p;
		for (; *p != '\0'; p++) {
			switch (*p) {
			case '#':
			case 'b':
			case '=':
				if (p == q	/* 1st char or after a slash */
				 || (p != q + 1	/* or invert '\' behaviour */
				  && p[-1] == '\\'))
					break;

				/* set the accidentals as unused utf-8 values
				 * (see subs.c) */
				switch (*p) {
				case '#':
					*p = 0x01;
					break;
				case 'b':
					*p = 0x02;
					break;
				default:
/*				case '=': */
					*p = 0x03;
					break;
				}
				if (p[-1] == '\\') {
					p--;
					l = strlen(p);
					memmove(p, p + 1, l);
				}
				break;
			case ' ':
			case '/':
				q = p + 1;
				break;
			}
		}
	}

	/* set the offsets and widths */
/*fixme:utf8*/
	for (ix = 0, gch = s->gch; ix < MAXGCH; ix++, gch++) {
		if (gch->type == '\0')
			break;
		if (gch->type == '@')
			continue;		/* no width */
		p = s->text + gch->idx;
		str_font(gch->font);
		w = tex_str(p);
		gch->w = w; // + 4;
		switch (gch->type) {
		case '_':			/* below */
			xspc = w * GCHPRE;
			if (xspc > 8)
				xspc = 8;
			gch->x = -xspc;
			y_below -= h_ann;
			gch->y = y_below;
			break;
		case '^':			/* above */
			xspc = w * GCHPRE;
			if (xspc > 8)
				xspc = 8;
			gch->x = -xspc;
			y_above -= h_ann;
			gch->y = y_above;
			break;
		default:			/* guitar chord */
			xspc = w * GCHPRE;
			if (xspc > 8)
				xspc = 8;
			gch->x = -xspc;
			if (gch_place < 0) {	/* below */
				y_below -= h_gch;
				gch->y = y_below;
				if (box) {
					y_below -= 2;
					gch->y -= 1;
				}
			} else {
				y_above -= h_gch;
				gch->y = y_above;
				if (box) {
					y_above -= 2;
					gch->y -= 1;
				}
			}
			break;
		case '<':		/* left */
			gch->x = -(w + 6);
			y_left -= h_ann;
			gch->y = y_left;
			break;
		case '>':		/* right */
			gch->x = 6;
			y_right -= h_ann;
			gch->y = y_right;
			break;
		}
	}
}

Project: abcm2ps
File: parse.c

static void get_map(char *p)
{
	struct map *map;
	struct note_map *note_map;
	char *name, *q;
	int l, type, pit, acc;

	if (*p == '\0')
		return;

	/* map name */
	name = p;
	while (!isspace((unsigned char) *p) && *p != '\0')
		p++;
	l = p - name;

	/* base note */
	while (isspace((unsigned char) *p))
		p++;
	if (*p == '*') {
		type = MAP_ALL;
		p++;
	} else if (strncmp(p, "octave,", 7) == 0) {
		type = MAP_OCT;
		p += 7;
	} else if (strncmp(p, "key,", 4) == 0) {
		type = MAP_KEY;
		p += 4;
	} else if (strncmp(p, "all", 3) == 0) {
		type = MAP_ALL;
		while (!isspace((unsigned char) *p) && *p != '\0')
			p++;
	} else {
		type = MAP_ONE;
	}
	if (type != MAP_ALL) {
		p = parse_acc_pit(p, &pit, &acc);
		if (acc < 0)			// if error
			pit = acc = 0;
		if (type == MAP_OCT || type == MAP_KEY) {
			pit %= 7;
			if (type == MAP_KEY)
				acc = A_NULL;
		}
	} else {
		pit = acc = 0;
	}

	// get/create the map
	for (map = maps; map; map = map->next) {
		if (strncmp(name, map->name, l) == 0)
			break;
	}
	if (!map) {
		map = getarena(sizeof *map);
		map->next = maps;
		maps = map;
		map->name = getarena(l + 1);
		strncpy(map->name, name, l);
		map->name[l] = '\0';
		map->notes = NULL;
	}
	for (note_map = map->notes; note_map; note_map = note_map->next) {
		if (note_map->type == type
		 && note_map->pit == pit
		 && note_map->acc == acc)
			break;
	}
	if (!note_map) {
		note_map = getarena(sizeof *note_map);
		memset(note_map, 0, sizeof *note_map);
		note_map->next = map->notes;
		map->notes = note_map;
		note_map->type = type;
		note_map->pit = pit;
		note_map->acc = acc;
		note_map->print_pit = -128;
		note_map->color = -1;
	}

	/* try the optional 'print' and 'heads' parameters */
	while (isspace((unsigned char) *p))
		p++;
	if (*p == '\0')
		return;
	q = p;
	while (!isspace((unsigned char) *q) && *q != '\0') {
		if (*q == '=')
			break;
		q++;
	}
	if (isspace((unsigned char) *q) || *q == '\0') {
		if (*p != '*') {
			p = parse_acc_pit(p, &pit, &acc);
			if (acc >= 0) {
				note_map->print_pit = pit;
				note_map->print_acc = acc;
			}
			if (*p == '\0')
				return;
		}
		p = q;
		while (isspace((unsigned char) *p))
			p++;
		if (*p == '\0')
			return;
		q = p;
		while (!isspace((unsigned char) *q) && *q != '\0') {
			if (*q == '=')
				break;
			q++;
		}
		if (isspace((unsigned char) *q) || *q == '\0') {
			name = p;
			p = q;
			l = p - name;
			note_map->heads = getarena(l + 1);
			strncpy(note_map->heads, name, l);
			note_map->heads[l] = '\0';
		}
	}

	/* loop on the parameters */
	for (;;) {
		while (isspace((unsigned char) *p))
			p++;
		if (*p == '\0')
			break;
		if (strncmp(p, "heads=", 6) == 0) {
			p += 6;
			name = p;
			while (!isspace((unsigned char) *p) && *p != '\0')
				p++;
			l = p - name;
			note_map->heads = getarena(l + 1);
			strncpy(note_map->heads, name, l);
			note_map->heads[l] = '\0';
		} else if (strncmp(p, "print=", 6) == 0) {
			p += 6;
			p = parse_acc_pit(p, &pit, &acc);
			if (acc >= 0) {
				note_map->print_pit = pit;
				note_map->print_acc = acc;
			}
		} else if (strncmp(p, "color=", 6) == 0) {
			int color;

			color = get_color(p + 6);
			if (color < 0) {
				error(1, NULL, "Bad color in %%%%map");
				return;
			}
			note_map->color = color;
		}
		while (!isspace((unsigned char) *p) && *p != '\0')
			p++;
	}
}

Project: abcm2ps
File: parse.c

static struct SYMBOL *process_pscomment(struct SYMBOL *s)
{
	char w[32], *p, *q;
	int voice;
	float h1;
	int lock = 0;

	p = s->text + 2;		/* skip '%%' */
	q = p + strlen(p) - 5;
	if (q > p
	 && strncmp(q, " lock", 5) == 0) {
		lock = 1;
		*q = '\0';
	}
	p = get_str(w, p, sizeof w);
	if (s->state == ABC_S_HEAD
	 && !check_header(s)) {
		error(1, s, "Cannot have %%%%%s in tune header", w);
		return s;
	}
	switch (w[0]) {
	case 'b':
		if (strcmp(w, "beginps") == 0
		 || strcmp(w, "beginsvg") == 0) {
			char use;

			if (w[5] == 'p') {
				if (strncmp(p, "svg", 3) == 0)
					use = 's';
				else if (strncmp(p, "nosvg", 5) == 0)
					use = 'p';
				else
					use = 'b';
			} else {
				use = 'g';
			}
			p = s->text + 2 + 7;
			while (*p != '\0' && *p != '\n')
				p++;
			if (*p == '\0')
				return s;		/* empty */
			ps_def(s, p + 1, use);
			return s;
		}
		if (strcmp(w, "begintext") == 0) {
			int job;

			if (s->state == ABC_S_TUNE) {
				if (!multicol_start)
					gen_ly(1);
			} else if (s->state == ABC_S_GLOBAL) {
				if (epsf || !in_fname)
					return s;
			}
			p = s->text + 2 + 9;
			while (*p == ' ' || *p == '\t')
				p++;
			if (*p != '\n') {
				job = get_textopt(p);
				while (*p != '\0' && *p != '\n')
					p++;
				if (*p == '\0')
					return s;	/* empty */
			} else {
				job = cfmt.textoption;
			}
			if (job != T_SKIP) {
				p++;
				write_text(w, p, job);
			}
			return s;
		}
		if (strcmp(w, "break") == 0) {
			struct brk_s *brk;

			if (s->state != ABC_S_HEAD) {
				error(1, s, "%%%%%s ignored", w);
				return s;
			}
			if (*p == '\0')
				return s;
			for (;;) {
				brk = malloc(sizeof *brk);
				p = get_symsel(&brk->symsel, p);
				if (!p) {
					error(1, s, "Bad selection in %%%%%s", w);
					return s;
				}
				brk->next = brks;
				brks = brk;
				if (*p != ',' && *p != ' ')
					break;
				p++;
			}
			return s;
		}
		break;
	case 'c':
		if (strcmp(w, "center") == 0)
			goto center;
		if (strcmp(w, "clef") == 0) {
			if (s->state != ABC_S_GLOBAL)
				clef_def(s);
			return s;
		}
		if (strcmp(w, "clip") == 0) {
			if (!cur_tune_opts) {
				error(1, s, "%%%%%s not in %%%%tune sequence", w);
				return s;
			}

			/* %%clip <symbol selection> "-" <symbol selection> */
			if (*p != '-') {
				p = get_symsel(&clip_start, p);
				if (!p) {
					error(1, s, "Bad start in %%%%%s", w);
					return s;
				}
				if (*p != '-') {
					error(1, s, "Lack of '-' in %%%%%s", w);
					return s;
				}
			}
			p++;
			p = get_symsel(&clip_end, p);
			if (!p) {
				error(1, s, "Bad end in %%%%%s", w);
				return s;
			}
			if (clip_start.bar < 0)
				clip_start.bar = 0;
			if (clip_end.bar < clip_start.bar
			 || (clip_end.bar == clip_start.bar
			  && clip_end.time <= clip_start.time)) {
				clip_end.bar = (short unsigned) ~0 >> 1;
			}
			return s;
		}
		break;
	case 'd':
		if (strcmp(w, "deco") == 0) {
			deco_add(p);
			return s;
		}
		if (strcmp(w, "dynamic") == 0) {
			set_voice_param(curvoice, s->state, w, p);
			return s;
		}
		break;
	case 'E':
		if (strcmp(w, "EPS") == 0) {
			float x1, y1, x2, y2;
			FILE *fp;
			char fn[STRL1], line[STRL1];

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

			/* get the bounding box */
			x1 = x2 = 0;
			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 (x1 == x2) {
				error(1, s, "No bounding box in '%s'", fn);
				return s;
			}
			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);
			}
			a2b("\001");	/* include file (must be the first after eob) */
			bskip(y2 - y1);
			a2b("%.2f %.2f%%%s\n", x1, -y1, fn);
			buffer_eob(0);
			return s;
		}
		break;
	case 'g':
		if (strcmp(w, "gchord") == 0
		 || strcmp(w, "gstemdir") == 0) {
			set_voice_param(curvoice, s->state, w, p);
			return s;
		}
		if (strcmp(w, "glyph") == 0) {
			if (!svg && epsf <= 1)
				glyph_add(p);
			return s;
		}
		break;
	case 'm':
		if (strcmp(w, "map") == 0) {
			get_map(p);
			return s;
		}
		if (strcmp(w, "maxsysstaffsep") == 0) {
			if (s->state != ABC_S_TUNE)
				break;
			parsys->voice[curvoice - voice_tb].maxsep = scan_u(p, 0);
			return s;
		}
		if (strcmp(w, "multicol") == 0) {
			float bposy;

			generate();
			if (strncmp(p, "start", 5) == 0) {
				if (!in_page)
					a2b("%%\n");	/* initialize the output */
				buffer_eob(0);
				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,
					      "%%%%%s new without start", w);
				} else {
					buffer_eob(0);
					bposy = get_bposy();
					if (bposy < multicol_start)
						bskip((bposy - multicol_start)
								/ cfmt.scale);
					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,
					      "%%%%%s end without start", w);
				} else {
					buffer_eob(0);
					bposy = get_bposy();
					if (bposy > multicol_max)
						bskip((bposy - multicol_max)
								/ cfmt.scale);
					else
						a2b("%%\n");	/* force write_buffer */
					cfmt.leftmargin = lmarg;
					cfmt.rightmargin = rmarg;
					multicol_start = 0;
					buffer_eob(0);
					if (!info['X' - 'A']
					 && !epsf)
						write_buffer();
				}
			} else {
				error(1, s,
				      "Unknown keyword '%s' in %%%%%s", p, w);
			}
			return s;
		}
		break;
	case 'M':
		if (strcmp(w, "MIDI") == 0
		 && strncmp(p, "temperamentequal", 16) == 0) {
			int n;

			if (cfmt.nedo) {
				error(1, s, "%%%%MIDI temperamentequal redefined");
				return s;
			}
			p += 16;
			while (isspace((unsigned char) *p))
				p++;
			n = atoi(p);
			if (n < 7 || n > 53) {
				error(1, s, "Bad value in %%%%MIDI temperamentequal");
				return s;
			}
			cfmt.nedo = n;
		}
		break;
	case 'n':
		if (strcmp(w, "newpage") == 0) {
			if (epsf || !in_fname)
				return s;
			if (s->state == ABC_S_TUNE)
				generate();
			buffer_eob(0);
			write_buffer();
//			use_buffer = 0;
			if (isdigit((unsigned char) *p))
				pagenum = atoi(p);
			close_page();
			if (s->state == ABC_S_TUNE)
				bskip(cfmt.topspace);
			return s;
		}
		break;
	case 'p':
		if (strcmp(w, "pos") == 0) {	// %%pos <type> <position>
			p = get_str(w, p, sizeof w);
			set_voice_param(curvoice, s->state, w, p);
			return s;
		}
		if (strcmp(w, "ps") == 0
		 || strcmp(w, "postscript") == 0) {
			ps_def(s, p, 'b');
			return s;
		}
		break;
	case 'o':
		if (strcmp(w, "ornament") == 0) {
			set_voice_param(curvoice, s->state, w, p);
			return s;
		}
		break;
	case 'r':
		if (strcmp(w, "repbra") == 0) {
			if (s->state != ABC_S_TUNE)
				return s;
			curvoice->norepbra = strchr("0FfNn", *p)
						|| *p == '\0';
			return s;
		}
		if (strcmp(w, "repeat") == 0) {
			int n, k;

			if (s->state != ABC_S_TUNE)
				return s;
			if (!curvoice->last_sym) {
				error(1, s,
				      "%%%s cannot start a tune", w);
				return s;
			}
			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 %%%%%s", w);
					return s;
				}
				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 %%%%%s", w);
						return s;
					}
				}
			}
			s->aux = REPEAT;
			if (curvoice->last_sym->type == BAR)
				s->doty = n;
			else
				s->doty = -n;
			sym_link(s, FMTCHG);
			s->nohdi1 = k;
			s->text = NULL;
			return s;
		}
		break;
	case 's':
		if (strcmp(w, "setbarnb") == 0) {
			if (s->state == ABC_S_TUNE) {
				struct SYMBOL *s2;
				int n;

				n = atoi(p);
				for (s2 = s->abc_next; s2; s2 = s2->abc_next) {
					if (s2->abc_type == ABC_T_BAR) {
						s2->aux = n;
						break;
					}
				}
				return s;
			}
			strcpy(w, "measurefirst");
			break;
		}
		if (strcmp(w, "sep") == 0) {
			float h2, len, lwidth;

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

			if (s->state != ABC_S_TUNE)
				return s;
			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 > (unsigned) nstaff) {
				error(1, s, "Bad staff in %%%%%s", w);
				return s;
			}
			curvoice->floating = 0;
			curvoice->cstaff = staff;
			return s;
		}
		if (strcmp(w, "staffbreak") == 0) {
			if (s->state != ABC_S_TUNE)
				return s;
			if (isdigit(*p)) {
				s->xmx = scan_u(p, 0);
				if (s->xmx < 0) {
					error(1, s, "Bad value in %%%%%s", w);
					return s;
				}
				if (p[strlen(p) - 1] == 'f')
					s->doty = 1;
			} else {
				s->xmx = 0.5 CM;
				if (*p == 'f')
					s->doty = 1;
			}
			sym_link(s, STBRK);
			return s;
		}
		if (strcmp(w, "stafflines") == 0) {
			if (isdigit((unsigned char) *p)) {
				switch (atoi(p)) {
				case 0: p = "..."; break;
				case 1: p = "..|"; break;
				case 2: p = ".||"; break;
				case 3: p = ".|||"; break;
				case 4: p = "||||"; break;
				case 5: p = "|||||"; break;
				case 6: p = "||||||"; break;
				case 7: p = "|||||||"; break;
				case 8: p = "||||||||"; break;
				default:
					error(1, s, "Bad number of lines");
					break;
				}
			} else {
				int l;

				l = strlen(p);
				q = p;
				p = getarena(l + 1);
				strcpy(p, q);
			}
			if (s->state != ABC_S_TUNE) {
				for (voice = 0; voice < MAXVOICE; voice++)
					voice_tb[voice].stafflines = p;
			} else {
				curvoice->stafflines = p;
			}
			return s;
		}
		if (strcmp(w, "staffscale") == 0) {
			char *q;
			float scale;

			scale = strtod(p, &q);
			if (scale < 0.3 || scale > 2
			 || (*q != '\0' && *q != ' ')) {
				error(1, s, "Bad value in %%%%%s", w);
				return s;
			}
			if (s->state != ABC_S_TUNE) {
				for (voice = 0; voice < MAXVOICE; voice++)
					voice_tb[voice].staffscale = scale;
			} else {
				curvoice->staffscale = scale;
			}
			return s;
		}
		if (strcmp(w, "staves") == 0
		 || strcmp(w, "score") == 0) {
			if (s->state == ABC_S_GLOBAL)
				return s;
			get_staves(s);
			return s;
		}
		if (strcmp(w, "stemdir") == 0) {
			set_voice_param(curvoice, s->state, w, p);
			return s;
		}
		if (strcmp(w, "sysstaffsep") == 0) {
			if (s->state != ABC_S_TUNE)
				break;
			parsys->voice[curvoice - voice_tb].sep = scan_u(p, 0);
			return s;
		}
		break;
	case 't':
		if (strcmp(w, "text") == 0) {
			int job;

center:
			if (s->state == ABC_S_TUNE) {
				gen_ly(1);
			} else if (s->state == ABC_S_GLOBAL) {
				if (epsf || !in_fname)
					return s;
			}
			if (w[0] == 'c') {
				job = T_CENTER;
			} else {
				job = cfmt.textoption;
				switch(job) {
				case T_SKIP:
					return s;
				case T_LEFT:
				case T_RIGHT:
				case T_CENTER:
					break;
				default:
					job = T_LEFT;
					break;
				}
			}
			write_text(w, p, job);
			return s;
		}
		if (strcmp(w, "tablature") == 0) {
			struct tblt_s *tblt;
			int i, j;

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

			switch (s->state) {
			case ABC_S_TUNE:
			case ABC_S_HEAD:
				for (i = 0; i < ncmdtblt; i++) {
					if (cmdtblts[i].active)
						continue;
					j = cmdtblts[i].index;
					if (j < 0 || tblts[j] == tblt)
						return s;
				}
				/* !! 2 tblts per voice !! */
				if (curvoice->tblts[0] == tblt
				 || curvoice->tblts[1] == tblt)
					break;
				if (curvoice->tblts[1]) {
					error(1, s,
						"Too many tablatures for voice %s",
						curvoice->id);
					break;
				}
				if (!curvoice->tblts[0])
					curvoice->tblts[0] = tblt;
				else
					curvoice->tblts[1] = tblt;
				break;
			}
			return s;
		}
		if (strcmp(w, "transpose") == 0) {
			struct VOICE_S *p_voice;
			struct SYMBOL *s2;
			int i, val;

			val = get_transpose(p);
			switch (s->state) {
			case ABC_S_GLOBAL:
				cfmt.transpose = val;
				return s;
			case ABC_S_HEAD: {
				cfmt.transpose += val;
				for (i = MAXVOICE, p_voice = voice_tb;
				     --i >= 0;
				     p_voice++) {
					p_voice->transpose = cfmt.transpose;
					memcpy(&p_voice->key, &p_voice->okey,
						sizeof p_voice->key);
					key_transpose(&p_voice->key);
					memcpy(&p_voice->ckey, &p_voice->key,
						sizeof p_voice->ckey);
					if (p_voice->key.empty)
						p_voice->key.sf = 0;
				}
				return s;
			    }
			}
			curvoice->transpose = cfmt.transpose + val;
			s2 = curvoice->sym;
			if (!s2) {
				memcpy(&curvoice->key, &curvoice->okey,
					sizeof curvoice->key);
				key_transpose(&curvoice->key);
				memcpy(&curvoice->ckey, &curvoice->key,
					sizeof curvoice->ckey);
				if (curvoice->key.empty)
					curvoice->key.sf = 0;
				return s;
			}
			for (;;) {
				if (s2->type == KEYSIG)
					break;
				if (s2->time == curvoice->time) {
					s2 = s2->prev;
					if (s2)
						continue;
				}
				s2 = s;
				s2->abc_type = ABC_T_INFO;
				s2->text = (char *) getarena(2);
				s2->text[0] = 'K';
				s2->text[1] = '\0';
				sym_link(s2, KEYSIG);
//				if (!curvoice->ckey.empty)
//					s2->aux = curvoice->ckey.sf;
				s2->aux = curvoice->key.sf;
				break;
			}
			memcpy(&s2->u.key, &curvoice->okey,
						sizeof s2->u.key);
			key_transpose(&s2->u.key);
			memcpy(&curvoice->ckey, &s2->u.key,
						sizeof curvoice->ckey);
			if (curvoice->key.empty)
				s2->u.key.sf = 0;
			return s;
		}
		if (strcmp(w, "tune") == 0) {
			struct SYMBOL *s2, *s3;
			struct tune_opt_s *opt, *opt2;

			if (s->state != ABC_S_GLOBAL) {
				error(1, s, "%%%%%s ignored", w);
				return s;
			}

			/* if void %%tune, remove all tune options */
			if (*p == '\0') {
				opt = tune_opts;
				while (opt) {
					free_voice_opt(opt->voice_opts);
					opt2 = opt->next;
					free(opt);
					opt = opt2;
				}
				tune_opts = NULL;
				return s;
			}

			if (strcmp(p, "end") == 0)
				return s;	/* end of previous %%tune */

			/* search the end of the tune options */
			s2 = s;
			for (;;) {
				s3 = s2->abc_next;
				if (!s3)
					break;
				if (s3->abc_type != ABC_T_NULL
				 && (s3->abc_type != ABC_T_PSCOM
				  || strncmp(&s3->text[2], "tune ", 5) == 0))
					break;
				s2 = s3;
			}

			/* search if already a same %%tune */
			opt2 = NULL;
			for (opt = tune_opts; opt; opt = opt->next) {
				if (strcmp(opt->s->text, s->text) == 0)
					break;
				opt2 = opt;
			}

			if (opt) {
				free_voice_opt(opt->voice_opts);
				if (s2 == s) {			/* no option */
					if (!opt2)
						tune_opts = opt->next;
					else
						opt2->next = opt->next;
					free(opt);
					return s;
				}
				opt->voice_opts = NULL;
			} else {
				if (s2 == s)			/* no option */
					return s;
				opt = malloc(sizeof *opt);
				memset(opt, 0, sizeof *opt);
				opt->next = tune_opts;
				tune_opts = opt;
			}

			/* link the options */
			opt->s = s3 = s;
			cur_tune_opts = opt;
			s = s->abc_next;
			for (;;) {
				if (s->abc_type != ABC_T_PSCOM)
					continue;
				if (strncmp(&s->text[2], "voice ", 6) == 0) {
					s = process_pscomment(s);
				} else {
					s->state = ABC_S_HEAD;

					/* !! no reverse link !! */
					s3->next = s;
					s3 = s;
				}
				if (s == s2)
					break;
				s = s->abc_next;
			}
			cur_tune_opts = NULL;
			return s;
		}
		break;
	case 'u':
		if (strcmp(w, "user") == 0) {
			deco[s->u.user.symbol] = parse.deco_tb[s->u.user.value - 128];
			return s;
		}
		break;
	case 'v':
		if (strcmp(w, "vocal") == 0) {
			set_voice_param(curvoice, s->state, w, p);
			return s;
		}
		if (strcmp(w, "voice") == 0) {
			struct SYMBOL *s2, *s3;
			struct voice_opt_s *opt, *opt2;

			if (s->state != ABC_S_GLOBAL) {
				error(1, s, "%%%%voice ignored");
				return s;
			}

			/* if void %%voice, free all voice options */
			if (*p == '\0') {
				if (cur_tune_opts) {
					free_voice_opt(cur_tune_opts->voice_opts);
					cur_tune_opts->voice_opts = NULL;
				} else {
					free_voice_opt(voice_opts);
					voice_opts = NULL;
				}
				return s;
			}

			if (strcmp(p, "end") == 0)
				return s;	/* end of previous %%voice */

			if (cur_tune_opts)
				opt = cur_tune_opts->voice_opts;
			else
				opt = voice_opts;

			/* search the end of the voice options */
			s2 = s;
			for (;;) {
				s3 = s2->abc_next;
				if (!s3)
					break;
				if (s3->abc_type != ABC_T_NULL
				 && (s3->abc_type != ABC_T_PSCOM
				  || strncmp(&s3->text[2], "score ", 6) == 0
				  || strncmp(&s3->text[2], "staves ", 7) == 0
				  || strncmp(&s3->text[2], "tune ", 5) == 0
				  || strncmp(&s3->text[2], "voice ", 6) == 0))
					break;
				s2 = s3;
			}

			/* if already the same %%voice
			 * remove the options */
			opt2 = NULL;
			for ( ; opt; opt = opt->next) {
				if (strcmp(opt->s->text, s->text) == 0) {
					if (!opt2) {
						if (cur_tune_opts)
							cur_tune_opts->voice_opts = NULL;
						else
							voice_opts = NULL;
					} else {
						opt2->next = opt->next;
					}
					free(opt);
					break;
				}
				opt2 = opt;
			}
			if (s2 == s)		/* no option */
				return s;
			opt = malloc(sizeof *opt + strlen(p));
			memset(opt, 0, sizeof *opt);
			if (cur_tune_opts) {
				opt->next = cur_tune_opts->voice_opts;
				cur_tune_opts->voice_opts = opt;
			} else {
				opt->next = voice_opts;
				voice_opts = opt;
			}

			/* link the options */
			opt->s = s3 = s;
			for ( ; s != s2; s = s->abc_next) {
				if (s->abc_next->abc_type != ABC_T_PSCOM)
					continue;
				s->abc_next->state = ABC_S_TUNE;
				s3->next = s->abc_next;
				s3 = s3->next;
			}
			return s;
		}
		if (strcmp(w, "voicecolor") == 0) {
			int color;

			if (!curvoice)
				return s;

			color = get_color(p);
			if (color < 0)
				error(1, s, "Bad color in %%%%voicecolor");
			else
				curvoice->color = color;
			return s;
		}
		if (strcmp(w, "voicecombine") == 0) {
			int combine;

			if (sscanf(p, "%d", &combine) != 1) {
				error(1, s, "Bad value in %%%%voicecombine");
				return s;
			}
			switch (s->state) {
			case ABC_S_GLOBAL:
				cfmt.combinevoices = combine;
				break;
			case ABC_S_HEAD:
				for (voice = 0; voice < MAXVOICE; voice++)
					voice_tb[voice].combine = combine;
				break;
			default:
				curvoice->combine = combine;
				break;
			}
			return s;
		}
		if (strcmp(w, "voicemap") == 0) {
			if (s->state != ABC_S_TUNE) {
				for (voice = 0; voice < MAXVOICE; voice++)
					voice_tb[voice].map_name = p;
			} else {
				curvoice->map_name = p;
			}
			return s;
		}
		if (strcmp(w, "voicescale") == 0) {
			char *q;
			float scale;

			scale = strtod(p, &q);
			if (scale < 0.6 || scale > 1.5
			 || (*q != '\0' && *q != ' ')) {
				error(1, s, "Bad %%%%voicescale value");
				return s;
			}
			if (s->state != ABC_S_TUNE) {
				for (voice = 0; voice < MAXVOICE; voice++)
					voice_tb[voice].scale = scale;
			} else {
				curvoice->scale = scale;
			}
			return s;
		}
		if (strcmp(w, "volume") == 0) {
			set_voice_param(curvoice, s->state, w, p);
			return s;
		}
		if (strcmp(w, "vskip") == 0) {
			if (s->state == ABC_S_TUNE) {
				gen_ly(0);
			} else if (s->state == ABC_S_GLOBAL) {
				if (epsf || !in_fname)
					return s;
			}
			bskip(scan_u(p, 0));
			buffer_eob(0);
			return s;
		}
		break;
	}
	if (s->state == ABC_S_TUNE) {
		if (strcmp(w, "leftmargin") == 0
		 || strcmp(w, "rightmargin") == 0
		 || strcmp(w, "scale") == 0) {
			generate();
			block_put();
		}
	}
	interpret_fmt_line(w, p, lock);
	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;
		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;
		}
		i--;
		voice_tb[i].next = NULL;
		parsys->nstaff = nstaff = i;
	}
	return s;
}

Project: abcm2ps
File: parse.c

static void gch_transpose(struct SYMBOL *s)
{
	int in_ch = 0;
	int i2 = curvoice->ckey.sf - curvoice->okey.sf;
	char *o = s->text, *p = o;

	// search the chord symbols
	for (;;) {
		if (in_ch || !strchr("^_<>@", *p)) {
			gch_tr1(s, p - s->text, i2);
			p = s->text + (p - o);
			o = s->text;
			for (p++; *p; p++) {
				if (strchr("\t;\n", *p))
					break;
			}
			if (!*p)
				break;
			switch (*p) {
			case '\t':
				in_ch = 1;
				break;
			case ';':
				in_ch = !strchr("^_<>@", p[1]);
				break;
			default:
				in_ch = 0;
				break;
			}
		} else {
			p = strchr(p, '\n');
			if (!p)
				break;
		}
		p++;
	}
}

Project: abcm2ps
File: parse.c

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";
static char txt_no_note[] = "No note in voice overlay";

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

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

	/* (here is treated a new overlay - '&') */
	/* create the extra voice if not done yet */
	if (!p_voice->last_sym) {
		error(1, s, txt_no_note);
		return;
	}
	p_voice->last_sym->sflags |= S_BEAM_END;
	voice2 = s->u.v_over.voice;
	p_voice2 = &voice_tb[voice2];
	if (parsys->voice[voice2].range < 0) {
		int clone;

		if (cfmt.abc2pscompat) {
			error(1, s, "Cannot have %%%%abc2pscompat");
			cfmt.abc2pscompat = 0;
		}
		clone = p_voice->clone >= 0;
		p_voice2->id[0] = '&';
		p_voice2->id[1] = '\0';
		p_voice2->second = 1;
		parsys->voice[voice2].second = 1;
		p_voice2->scale = p_voice->scale;
		p_voice2->octave = p_voice->octave;
		p_voice2->transpose = p_voice->transpose;
		memcpy(&p_voice2->key, &p_voice->key,
					sizeof p_voice2->key);
		memcpy(&p_voice2->ckey, &p_voice->ckey,
					sizeof p_voice2->ckey);
		memcpy(&p_voice2->okey, &p_voice->okey,
					sizeof p_voice2->okey);
		p_voice2->posit = p_voice->posit;
		p_voice2->staff = p_voice->staff;
		p_voice2->cstaff = p_voice->cstaff;
		p_voice2->color = p_voice->color;
		p_voice2->map_name = p_voice->map_name;
		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];
				strcpy(p_voice3->id, p_voice2->id);
				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 = voice;
		time = p_voice2->time;
		for (s = p_voice->last_sym; /*s*/; s = s->prev) {
			if (s->type == BAR
			 || s->time <= time)	/* (if start of tune) */
				break;
		}
		over_time = s->time;
	} else {
		if (over_mxtime == 0)
			over_mxtime = p_voice->time;
		else if (p_voice->time != over_mxtime)
			error(1, s, tx_wrong_dur);
	}
	p_voice2->time = over_time;
	curvoice = p_voice2;
}

Project: abcm2ps
File: parse.c

static void parse_path(char *p, char *q, char *id, int idsz)
{
	struct SYMBOL *s;
	char *buf, *r, *t, *op = NULL, *width, *scale, *trans;
	int i, fill, npar = 0;
	float x1, y1, x, y;
char *rmax;

	r = strstr(p, "class=\"");
	if (!r || r > q)
		return;
	r += 7;
	fill = strncmp(r, "fill", 4) == 0;
	width = strstr(p, "stroke-width:");
	scale = strstr(p, "scale(");
	if (scale && scale > q)
		scale = NULL;
	trans = strstr(p, "translate(");
	if (trans && trans > q)
		trans = NULL;
	for (;;) {
		p = strstr(p, "d=\"");
		if (!p)
			return;
		if (isspace((unsigned char) p[-1]))	// (check not 'id=..")
			break;
		p += 3;
	}
	i = (int) (q - p) * 4 + 200;		// estimated PS buffer size
	if (i > TEX_BUF_SZ)
		buf = malloc(i);
	else
		buf = tex_buf;
rmax=buf + i;
	r = buf;
	*r++ = '/';
	idsz -= 5;
	strncpy(r, id + 4, idsz);
	r += idsz;
	strcpy(r, "{gsave T ");
	r += strlen(r);
	if (scale || trans) {
		if (scale) {
			scale += 6;		// "scale("
			t = get_val(scale, &x1);
			if (*t == ',')
				t = get_val(t + 1, &y1);
			else
				y1 = x1;
		}
		if (trans) {
			trans += 10;		// "translate("
			t = get_val(trans, &x) + 1; //","
			t = get_val(t, &y);
		}
		if (!scale)
			r += sprintf(r, "%.2f %.2f T ", x, -y);
		else if (!trans)
			r += sprintf(r, "%.2f %.2f scale ", x1, y1);
		else if (scale > trans)
			r += sprintf(r, "%.2f %.2f T %.2f %.2f scale ",
					x, -y, x1, y1);
		else
			r += sprintf(r, "%.2f %.2f scale %.2f %.2f T ",
					x1, y1, x, -y);
	}
	strcpy(r, "0 0 M\n");
	r += strlen(r);
	if (width && width < q) {
		*r++ = ' ';
		width += 13;
		while (isdigit(*width) || *width == '.')
			*r++ = *width++;
		*r++ = ' ';
		*r++ = 'S';
		*r++ = 'L';
		*r++ = 'W';
	}
	p += 3;
	for (;;) {
		if (*p == '\0' || *p == '"')
			break;
		i = 0;
		switch (*p++) {
		default:
			if ((isdigit((unsigned char) p[-1]))
			 || p[-1] == '-' || p[-1] == '.') {
				if (!npar)
					continue;
				p--;			// same op
				break;
			}
			continue;
		case 'M':
			op = "M";
			npar = 2;
			break;
		case 'm':
			op = "RM";
			npar = 2;
			break;
		case 'L':
			op = "L";
			npar = 2;
			break;
		case 'l':
			op = "RL";
			npar = 2;
			break;
		case 'H':
			op = "H";
			npar = 1;
			break;
		case 'h':
			op = "h";
			npar = 1;
			break;
		case 'V':
			op = "V";
			npar = 1;
			break;
		case 'v':
			*r++ = ' ';
			*r++ = '0';
			op = "RL";
			i = 1;
			npar = 2;
			break;
		case 'z':
			op = "closepath";
			npar = 0;
			break;
		case 'C':
			op = "C";
			npar = 6;
			break;
		case 'c':
			op = "RC";
			npar = 6;
			break;
//		case 'A':
//			op = "arc";
//			break;
//		case 'a':
//			op = "arc";
//			break;
		case 'q':
			op = "RC";
			npar = 2;
			p = get_val(p, &x1);
			p = get_val(p, &y1);
			t = get_val(p, &x);
			t = get_val(t, &y);
			r += sprintf(r, " %.2f %.2f %.2f %.2f",
					x1*2/3, -y1*2/3,
					x1+(x-x1)*2/3, -y1-(y-y1)*2/3);
			break;
		case 't':
			op = "RC";
			npar = 2;
			x1 = x - x1;
			y1 = y - y1;
			t = get_val(p, &x);
			t = get_val(t, &y);
			r += sprintf(r, " %.2f %.2f %.2f %.2f",
					x1*2/3, -y1*2/3,
					x1+(x-x1)*2/3, -y1-(y-y1)*2/3);
			break;
		}
		*r++ = ' ';
		for ( ; i < npar; i++) {
			while (isspace((unsigned char) *p))
				p++;
			if (i & 1) {		// y is inverted
				if (*p == '-')
					p++;
				else if (*p != '0' || p[1] != ' ')
					*r++ = '-';
			}
			while ((isdigit((unsigned char) *p))
			    || *p == '-' || *p == '.')
				*r++ = *p++;
			*r++ = ' ';
		}
		if (*op == 'h') {
			*r++ = '0';
			*r++ = ' ';
			op = "RL";
		}
		strcpy(r, op);
		r += strlen(r);
if (r + 30 > rmax) bug("Buffer overflow in SVG to PS", 1);
	}
	strcpy(r, fill ? " fill" : " stroke");
	r += strlen(r);
	strcpy(r, "\ngrestore}!");
	r += strlen(r);

	s = getarena(sizeof(struct SYMBOL));
	memset(s, 0, sizeof(struct SYMBOL));
	s->text = getarena(strlen(buf) + 1);
	strcpy(s->text, buf);
	ps_def(s, s->text, 'p');
	if (buf != tex_buf)
		free(buf);
}

Project: abcm2ps
File: parse.c

static void get_staves(struct SYMBOL *s)
{
//	struct SYMBOL *s2;
	struct VOICE_S *p_voice, *p_voice2;
	struct staff_s *p_staff, staves[MAXVOICE];
	int i, flags, voice, staff, range, dup_voice, maxtime;

	memset(staves, 0, sizeof staves);
	parse_staves(s, staves);
	if (staves[0].voice < 0)			// if error
		return;

	voice_compress();
	voice_dup();

	/* create a new staff system */
	curvoice = p_voice = first_voice;
	maxtime = p_voice->time;
	flags = p_voice->sym != NULL;
	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)
			flags = 1;
	}
	if (flags == 0			/* if first %%staves */
	 || (maxtime == 0 && staves_found < 0)) {
		for (voice = 0; voice < MAXVOICE; voice++)
			parsys->voice[voice].range = -1;
	} else {

		/*
		 * create a new staff system and
		 * link the staves in a voice which is seen from
		 * the previous system - see sort_all
		 */
//		p_voice = curvoice;
		if (parsys->voice[curvoice - voice_tb].range < 0) {
			for (voice = 0; voice < MAXVOICE; voice++) {
				if (parsys->voice[voice].range >= 0) {
					curvoice = &voice_tb[voice];
					break;
				}
			}
/*fixme: should check if voice < MAXVOICE*/
		}
		curvoice->time = maxtime;

		// put the staves before a measure bar (see draw_bar())
//		s2 = curvoice->last_sym;
//		if (s2 && s2->type == BAR && s2->time == maxtime) {
//			curvoice->last_sym = s2->prev;
//			if (!curvoice->last_sym)
//				curvoice->sym = NULL;
//			sym_link(s, STAVES);
//			s->next = s2;
//			s2->prev = s;
//			curvoice->last_sym = s2;
//		} else {
			sym_link(s, STAVES); // link the staves in the current voice
//		}
		s->state = ABC_S_HEAD; /* (output PS sequences immediately) */
		parsys->nstaff = nstaff;
		system_new();
	}
	staves_found = maxtime;

	/* initialize the voices */
	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;
	}

	/* create the 'clone' voices */
	dup_voice = MAXVOICE;
	range = 0;
	p_staff = 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 = NULL;
			p_voice2->sym = p_voice2->last_sym = NULL;
			p_voice2->tblts[0] = p_voice2->tblts[1] = NULL;
			p_voice2->clone = -1;
			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->text[3] == 't') {		/* if %%staves */
		for (i = 0, p_staff = staves;
		     i < MAXVOICE - 2 && p_staff->voice >= 0;
		     i++, p_staff++) {
			flags = p_staff->flags;
			if (!(flags & (OPEN_BRACE | OPEN_BRACE2)))
				continue;
			if ((flags & (OPEN_BRACE | CLOSE_BRACE))
					== (OPEN_BRACE | CLOSE_BRACE)
			 || (flags & (OPEN_BRACE2 | CLOSE_BRACE2))
					== (OPEN_BRACE2 | CLOSE_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 = 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) {
			p_voice2 = p_voice;
			while (i < MAXVOICE) {
				i++;
				p_staff++;
				voice = p_staff->voice;
				p_voice = &voice_tb[voice];
				if (p_staff->flags & MASTER_VOICE) {
					p_voice2->second = 1;
					p_voice2 = p_voice;
				} else {
					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;
	parsys->nstaff = nstaff = staff;

	/* change the behaviour of '|' in %%score */
	if (s->text[3] == 'c') {		/* if %%score */
		for (staff = 0; staff < nstaff; staff++)
			parsys->staff[staff].flags ^= STOP_BAR;
	}

	for (voice = 0; voice < MAXVOICE; voice++) {
		p_voice = &voice_tb[voice];
		parsys->voice[voice].second = p_voice->second;
		staff = p_voice->staff;
		if (staff > 0)
			p_voice->norepbra
				= !(parsys->staff[staff - 1].flags & STOP_BAR);
		if (p_voice->floating && staff == nstaff)
			p_voice->floating = 0;
	}
	curvoice = &voice_tb[parsys->top_voice];
}

Project: abcm2ps
File: parse.c

static void set_bar_num(void)
{
	struct SYMBOL *s, *s2, *s3;
	int bar_time, wmeasure, tim;
	int bar_num, bar_rep;

	wmeasure = voice_tb[cursys->top_voice].meter.wmeasure;
	bar_rep = nbar;

	/* don't count a bar at start of line */
	for (s = tsfirst; ; s = s->ts_next) {
		if (!s)
			return;
		switch (s->type) {
		case TIMESIG:
		case CLEF:
		case KEYSIG:
		case FMTCHG:
		case STBRK:
			continue;
		case BAR:
			if (s->aux) {
				nbar = s->aux;		/* (%%setbarnb) */
				break;
			}
			if (s->u.bar.repeat_bar
			 && s->text
			 && !cfmt.contbarnb) {
				if (s->text[0] == '1') {
					bar_rep = nbar;
				} else {
					nbar = bar_rep; /* restart bar numbering */
					s->aux = nbar;
				}
			}
			break;
		}
		break;
	}

	/* set the measure number on the top bars
	 * and move the clefs before the measure bars */
	bar_time = s->time + wmeasure;	/* for incomplete measure at start of tune */
	bar_num = nbar;
	for ( ; s; s = s->ts_next) {
		switch (s->type) {
		case CLEF:
			if (s->sflags & S_NEW_SY)
				break;
			for (s2 = s->ts_prev; s2; s2 = s2->ts_prev) {
				if (s2->sflags & S_NEW_SY) {
					s2 = NULL;
					break;
				}
				switch (s2->type) {
				case BAR:
					if (s2->sflags & S_SEQST)
						break;
					continue;
				case MREST:
				case NOTEREST:
				case SPACE:
				case STBRK:
				case TUPLET:
					s2 = NULL;
					break;
				default:
					continue;
				}
				break;
			}
			if (!s2)
				break;

			/* move the clef */
			s->next->prev = s->prev;
			s->prev->next = s->next;
			s->ts_next->ts_prev = s->ts_prev;
			s->ts_prev->ts_next = s->ts_next;
			s->next = s2;
			s->prev = s2->prev;
			if (s->prev)
				s->prev->next = s;
			s2->prev = s;
			s->ts_next = s2;
			s->ts_prev = s2->ts_prev;
			if (s->ts_prev)
				s->ts_prev->ts_next = s;
			s2->ts_prev = s;
//			if (s->sflags & S_NEW_SY) {
//				s->sflags &= ~S_NEW_SY;
//				s->ts_next->sflags |= S_NEW_SY;
//			}
			s3 = s->extra;
			if (s3) {
				if (s->ts_next->extra) {
					while (s3->next)
						s3 = s3->next;
					s3->next = s->ts_next->extra;
					s->ts_next->extra = s->extra;
				} else {
					s->ts_next->extra = s3;
				}
				s->extra = NULL;
			}
			s = s2;
			break;
		case TIMESIG:
			wmeasure = s->u.meter.wmeasure;
			if (s->time < bar_time)
				bar_time = s->time + wmeasure;
			break;
		case MREST:
			bar_num += s->u.bar.len - 1;
			while (s->ts_next
			    && s->ts_next->type != BAR)
				s = s->ts_next;
			break;
		case BAR:
//			if (s->flags & ABC_F_INVIS)
//				break;
			if (s->aux) {
				bar_num = s->aux;		/* (%%setbarnb) */
//				if (s->time < bar_time) {
//					s->aux = 0;
					break;
//				}
			} else {
				if (s->time < bar_time)	/* incomplete measure */
					break;
				bar_num++;
			}

			/* check if any repeat bar at this time */
			tim = s->time;
			s2 = s;
			do {
				if (s2->type == BAR
				 && s2->u.bar.repeat_bar
				 && s2->text
				 && !cfmt.contbarnb) {
					if (s2->text[0] == '1')
						bar_rep = bar_num;
					else		/* restart bar numbering */
						bar_num = bar_rep;
					break;
				}
				s2 = s2->next;
			} while (s2 && s2->time == tim);
			s->aux = bar_num;
			bar_time = s->time + wmeasure;
			break;
		}
	}

	/* do the %%clip stuff */
	if (clip_start.bar >= 0) {
		if (bar_num <= clip_start.bar
		 || nbar > clip_end.bar) {
			tsfirst = NULL;
			return;
		}
		do_clip();
	}

	/* do the %%break stuff */
	{
		struct brk_s *brk;
		int nbar_min;

//		if (nbar == 1)
//			nbar = -1;	/* see go_global_time */
		nbar_min = nbar;
		if (nbar_min == 1)
			nbar_min = -1;
		for (brk = brks; brk; brk = brk->next) {
			if (brk->symsel.bar <= nbar_min
			 || brk->symsel.bar > bar_num)
				continue;
			s = go_global_time(tsfirst, &brk->symsel);
			if (s)
				s->sflags |= S_EOLN;
		}
	}
	if (cfmt.measurenb < 0)		/* if no display of measure bar */
		nbar = bar_num;		/* update in case of more music to come */
}

Project: abcm2ps
File: parse.c

static char *get_val(char *p, float *v)
{
	char tmp[32], *r = tmp;

	while (isspace((unsigned char) *p))
		p++;
	while ((isdigit((unsigned char) *p) && r < &tmp[32 - 1])
	    || *p == '-' || *p == '.')
		*r++ = *p++;
	*r = '\0';
	sscanf(tmp, "%f", v);
	return p;
}