Welcome To Our Shell

Mister Spy & Souheyl Bypass Shell

Current Path : /proc/thread-self/root/usr/local/lib/python3.8/dist-packages/iftlib/abc/56/

Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
Upload File :
Current File : //proc/thread-self/root/usr/local/lib/python3.8/dist-packages/iftlib/abc/56/abcm2ps-8.5.2.tar

abcm2ps-8.5.2/Changes0000644000175000017500000003207312462105155012450 0ustar  jefjef---- Version 8.5.2 - 15/01/28 ----

Fix bad display when bad ties to grace notes
	(reported by Willem).
Change the default fonts to serif and sans-serif
		instead of Times and Helvetica when SVG output.
Fix bad guitar box when many guitar chords on a note.
Fix bad beams when tremolo inside beamed notes
	(reported by David Lacroix).
Fix loss of ABC lines after %%abc-include when %%select did not
		match the last included tune.
Fix clash of accidentals with stems in some cases of full voice overlap.
Fix chinese.abc for correct rendering with pango.
Fix some compilation warnings
	(reported by Daniel Branning).
Fix bad measure number when the bar followed by a clef change since 8.3.5
	(reported by Frédéric Boulanger).
Fix loss of first note when the tune starts with %%ps since 8.5.0
	(reported by David Lacroix).
Fix bad font-family with SVG output since 8.5.1.

---- Version 8.5.1 - 15/01/05 ----

Fix bad handling of voice overlap when at leat one unison.
Fix bad extra space at end of line when clef change by K:.
Add file glyphs.abc in the package.
Fix loss of font change before non-ASCII characters in strings.
Fix bad offsets and fonts of annotation-like decorations.
Fix loop on %%voice after %%voice since 8.3.2.
Fix erroneous display of rests in empty staves when "%%staffnonote 0".

---- Version 8.5.0 - 15/01/02 ----

Fix bad handling of font-style when no '-' in font name.
Extend %%deco with annotation-like strings.
Extend "%%setfont-x" up to 9.
Add %%voicecolor
	(asked by Frédéric Boulanger).
Fix "setgray" in SVG output.
Add some styles (preserve white spaces, svg as block) on XHTML output ('-X').
Remove '<p>' from '-v -O -' output (many SVG images on stdout).
Do smaller shifts when voice overlap
	(asked by David Lacroix).
Fix bad unisons when different accidentals
	(reported by David Lacroix).
Fix loss of key signatures and meters when voice in empty staff
	(reported by Willem).
Fix some cases of music lines cut inside beamed notes.
Adjust the position of the 'crdc' decorations and update deco.abc.
Fix loss of the 'svg' definition used by the PostScript code on SVG output.
Fix again bad display of unisons
	(reported by David Lacroix).
Apply %%topspace when %%newpage inside a tune
	(reported by Johann Schatzer).

---- Version 8.4.0 - 14/12/16 ----

Remove features.txt from the tarball.
Fix "%%microscale 4" on SVG output.
Handle %%microscale.
Add '+' in %%score/%%staves.
Fix bad handling of "K:none", especially when transposition.
Fix bad note heads when %%combinevoices
	(reported by Johann Schatzer).
Fix bad transposition of the latin guitar chord "La".
Fix loss of 'exp none' in K:.
Fix bad display of unisons
	(reported by David Lacroix).
Fix some compilation warnings
	(reported by John Chambers).
Fix loss of some clef warnings at end of previous lines.

---- Version 8.3.4 - 14/12/02 ----

Fix double clefs when new voices in %%staves
	(reported by Francis Stevens).
Fix loss of 128 decorations per tune since 8.3.0
	(reported by Henry Bley-Vroman).
Fix problems with decorations or guitar chords/annotations
		in sequence/measure repeat.
	(reported by Frédéric Boulanger).

---- Version 8.3.3 - 14/11/26 ----

Fix no display of lyrics after tune since 8.3.2
	(reported by Gerhard Schacherl).
Fix bad position of grace notes in overlay voices at start of measure
	(reported by Jean-Luc Zins).
Fix memory leak relative to decorations.
Set back %%autoclef
	(asked by Atte Jensen).
Remove the references to 'format.txt'
	(reported by Martin Tarenskeen).
Fix bad repeat font when staves/voices with different scales
	(reported by Gerhard Schacherl).

---- Version 8.3.2 - 14/11/01 ----

Change the PS font scale of the accidentals according to the FontMatrix
	(reported by Chuck Boody).
Remove format.txt from the tarball.
Add decoration function 'crdc'.
Fix loss of voice options when many %%voice's without "%%voice end" in %%tune.
Fix loss of lyrics when ABC version 2.1 and generation restart.
Accept '<note1><note2>' in %%transpose.
Fix bad note transposition since 8.2.0.

---- Version 8.3.1 - 14/10/14 ----

Fix crash when %%combinevoices on beamed notes since 8.1.5
	(reported by David Lacroix).
Fix bad start/stop of ties since 8.1.0.
Fix abnormal error when '<none>' or '<EOL>' in %%linebreak ending with 'lock'.
Fix abnormal error when spaces after backslash in music lines
	(reported by Daniel Branning).
Don't let vertical space when all staves are empty.

---- Version 8.3.0 - 14/10/05 ----

Fix program loop when no measure bar at end of line since 8.2.2.
Fix some clashes of rests when more than 2 voices per staff.
Don't reset the staff to the main voice when finding a previous
	overlaid voice.
Fix the documentation about %%stretchlast and change its default
	value from 0.2 to 0.25.
Handle utf-8 in the names of the notes/history/.. after tune
	(reported by Hudson Flávio Meneses Lacerda).
Fix crash when line break before a note alone in a staff
	and a new voice since 8.2.0.
Fix crash when %%alignbars since 8.2.0.
Fix some clashes of dots and stems when voice overlap
	(reported by Hudson Flávio Meneses Lacerda a long time ago!).
Fix bad PS output when exact minimum spacing since 8.2.1.
Fix crash when tune starting with grace note in the middle voice
	of a 3-voices staff.
Fix crash when many %%staves/%%score and new pages
	(reported by David Lacroix).
Don't print the lyrics after tune when "%%writefields W n".
Fix loss of explicit clefs since 8.2.0.
Fix M$ compiler errors
	(reported by Olivier Levon).
Simplify the ABC parser.

---- Version 8.2.2 - 14/09/12 ----

Fix crash when slur of second voice continued on next line.
Fix bad auto clef after generation restart when clefs inserted.
Split the music lines at better places.
Fix many errors due to the lack of clefs in secondary voices.
Fix abnormal 'Bad tie' errors when tie on 2 music lines
	(reported by Olivier Levon).
Fix compilation problems with VS Studio 2013 Express
	(reported by Olivier Levon).
Fix generation abort when lack of V: after %%staves/%%score
	(reported by Olivier Levon).

---- Version 8.2.1 - 14/09/03 ----

Fix crash when forced clef after %%staves/%%score since 8.2.0
	(reported by Francis Stevens and Olivier Levon).

---- Version 8.2.0 - 14/08/29 ----

Fix bad clefs when voice/staff changes in %%staves/%%score.
Remove %%autoclef which was not used.
Fix loss of indentation since 8.1.0
	(reported by David Lacroix).
Change the internal treatment of %%score/%%staves for no extra space.
Add !marcato! and !^!
	(asked in the ABC mailing list).
Fix bad stem direction in lowest voice when invisible and visible rests
		at a same time in a measure since 8.1.0
	(reported by Eric Reinbold).
Set lowest value of %%staffscale to 0.3
	(asked by Mike Scott).
Accept '^' and '$' as beginning and end of line in tune select.
Fix crash when multi-rest at start of a second voice of a staff.

---- Version 8.1.5 - 14/08/09 ----

Fix crash when %%combinevoices and different beaming.
Fix lack of ledger lines when large chord.
Always remove the invisible rests at start of tune when L:auto.
Add !stemless!.
Fix abnormal error 'Bad tie' when tie at end of voice
	(reported by Daniel Branning).
Fix some compilation warnings
	(reported by Daniel Branning).
Fix bad handling of !()! since 8.1.4
	(reported by David Lacroix).
Fix horizontal offset of the custodes
	(asked by Stephen West).
Fix crash when %%continueall and no measure bar in the tune.
Fix bad horizontal offset of rests alone in a measure
	(reported by Stephen West).

---- Version 8.1.4 - 14/07/17 ----

Fix lack of source last character in syntax errors.
Change the definition of the start of long decorations for correct
	display when the ending decoration is not in the tune.
Set the extra accidentals of K: at their indicated pitches
	(asked by Paul Morrisset).
Fix double interpretation of '.' in "!p!.(c"
	(reported by David Lacroix).
Fix lack of vertical space when empty lines in %%begintext
	(reported by Henry Bley-Vroman).
Fix bad PostScript output when %%begintext justify.
Output the PDF marks of all T:'s, even if the first one is empty
	(asked by David Lacroix).
Fix the size of font of the page header/footer, broken in 8.1.3
	(reported by Francis Stevens).

---- Version 8.1.3 - 14/07/01 ----

Add more information about the titles/subtitles in the PostScript
		output for (external) index generation
	(asked by Tim Macdonald).
Add Δ (delta) in the known glyphs
	(asked by Chuck Boody).
Fix some bad 'Line overfull' messages when automatic line break.
Fix bad clef when L:auto and quick clef change at start of tune.
Invert the behaviour of '\' in the guitar chords
	(asked by David Lacroix).
Fix bad interval between staff systems according to %%staffsep
	(reported by Eric Reinbold and David Lacroix).
Change <p> to <br/> between images on SVG output.
Fix bad %%header/%%footer vertical offsets.
Don't put the last music line on a new page when page overflow on W:.

---- Version 8.1.2 - 14/06/20 ----

Fix bad vertical offset of dots sometimes when many voices per staff.
Don't get 'auto' alone as 'clef=auto'
	(reported by David Lacroix).
Fix double clef at start of staff when many voices in the staff
		since 8.1.0.
	(reported by David Lacroix).

---- Version 8.1.1 - 14/06/19 ----

Fix crash when a voice with no note appears as second voice of a staff
		since 8.1.0
	(reported by Francis Stevens).

---- Version 8.1.0 - 14/06/19 ----

Split the tune into music lines at better places.
Fix clash of voice name with staff when new voice after %%staves.
Fix bad music line indentation when more staves further in the tune.
Set the ties closer to the notes.
Fix bad automatic clef changes.
Add '%%clef auto'.
Change internal treatment of clefs.
Fix clash of notes in chords when unison or second with long notes.
Fix bad horizontal offset of accidents on long notes.
Fix bad key signature after change to 'K:none'.
Fix bad handling of '|' when the first character in w:
	(reported by Alex Scheutzow).
Change internal treatment of #lines and scale of the staves.
Fix bad clef/note offset when a voice with explicit clef moves
		to an other staff
	(reported by  reinier maliepaard).
Fix bad font for !trill!
	(reported by David Lacroix).
Check the floating voices in %%score/%%staves.
Fix bad stem direction when %%combinevoices.
Fix crash when unknown decoration on one character since 8.0.4.
Fix crash when measure bar numbering and less notes in the upper staff
	(reported by : J.Joao Almeida).
Add 'cue=' in K: and V:
	(asked by Alex Scheutzow).
Fix too big length of syntax error messages
	(reported by David Lacroix).
Fix bad horizontal offset of dots in chords since 8.0.4.
Fix bad horizontal offset of full rests in voice overlay
	(reported by J.C.L.).

---- Version 8.0.4 - 14/05/23 ----

Fix too wide space between text paragraphs when not fill or justify.
Fix crash when utf-8 and latin characters in a same file
	(reported by Henry Bley-Vroman).
Fix note/accidental clash when chords with unisons and seconds
	(reported by David Lacroix).
Change internal fonts to 'serif' for SVG output.
Forbid rests in grace note sequences.
Fix crash when rest in grace note sequence.
Fix direction of ties in chords with odd number of notes.
Fix bad beams in SVG output
	(reported by David Lacroix).
Fix bad direction of beam stub on last note when !beambr1! / !beambr2!
	(reported by David Lacroix).
May have up to 128 decorations per tune
	(asked by Henry Bley-Vroman).
Fix crash when only P: or Q: in a generation sequence
	(reported by Henry Bley-Vroman).

---- Version 8.0.3 - 14/05/05 ----

Fix non-function of %%voice inside %%tune since some 7.5.x
	(reported by Gerhard Schacherl).
Fix small internal problem with decorations.
Fix position of accent marks (!>!)
	(reported by Paul Rosen).
Fix bad margins when tune inside %%multicol
	(reported by A.B. Steen).
Don't draw repeat brackets when bar between 2 staves.
Fix compilation warning on MS-windows
	(reported/fixed by Olivier Levon and Jean-Luc Zins).
Don't display an accidental when {#,b,=} at start of a word in guitar chords.
Fix lack of EOS in some internal strings
	(fixed by Olivier Levon).
Fix bad guitar chord / annotation when '\' inside.
Fix double transposition of 2 octaves when both %%abc2pscompat
	and octave= in K:/V:.
Fix compilation warnings on MAC
	(reported by Chuck Boody).

---- Version 8.0.2 - 14/04/02 ----

Fix bad text justication with PostScript output
	(reported by Gerhard Schacherl).
Fix loss of positions (decorations, lyrics..) when many tunes
	(reported by John Taylor).
Add HAVE_MMAP in config.h for ms-windows users
	(reported by Jean-Luc Zins).
Fix loss of tie at end of line
	(reported by David Lacroix).
Add '--param=value' in 'configure'.
Fix bad configure when full path to the cc compiler
	(reported by Olivier Levon).

---- Version 8.0.1 - 14/03/31 ----

Fix bad configure when pango is found (Makefile empty)
	(reported by Martin Tarenskeen).

---- Version 8.0.0 - 14/03/29 ----

Remove some vertical space in tune header when no composer.
Remove the vertical space of the empty staves.
Remove the GNU 'autoconf' stuff (see 'INSTALL').
Remove the License.
Change the format of the error messages.
Remove tight.fmt.
Add sample8.html.
Add command line option '-z' for ABC embedded in HTML/XHTML.
Fix loss of vertical space above the staves (%%staffsep) since 7.6.10
	(reported by David Lacroix).
Creation from abcm2ps version 7.8.0.
abcm2ps-8.5.2/INSTALL0000644000175000017500000000454012316761417012213 0ustar  jefjef		--- abcm2ps installation ---


Unix(-like) systems
===================

This version does not use the GNU 'autoconf' package anymore.

The furnished 'Makefile' should work as it is for most systems.
So, simply do

	make

and then, as root, do:

	make install

If you are not happy with the Makefile options (install programm,
installation directories..), you may create a file 'custom' with
your own values (Bourne shell syntax) and use the script 'configure'
to create an other Makefile.
The variables which may be customized are at the head of the script
configure. For example, if you prefer to use 'clang' instead of 'gcc',
the file 'custom' would contain the single line

	CC=clang

then, you do

	./configure
	make

Without a file 'custom', you may do the same with

	./configure --CC=clang

If you want to use the 'pango' library, this one is not enabled
by default in the Makefile and you should use script 'configure'
to include it in the Makefile.


Windows or MAC systems
======================

Change the file 'config.h' according to your preferences, compile all
the '.c' files using an ANSI C compiler, and link them together.
The resulting binary file should run immediately from where it has been
generated. You may then move it at any place you want.

Note: I don't know how to do file mapping (mmap) in ms-windows, so,
you must comment 'HAVE_MMAP' in config.h.


Testing
=======

To test the program, run it with one of the .abc files as the command
line argument:

	abcm2ps sample

The resulting file, 'Out.ps', may be displayed using a PostScript
previewer such as ghostscript or zathura, or it may be sent directly
to a PostScript printer, or indirectly to a simple printer using
a postscript filter.


About the 'pango' library
=========================

abcm2ps may use the 'pango' library to render texts with non latin
characters on PostScript output. If you have no such texts or
if you do only SVG/(X)HTML output, you don't need this library.

In Unix(-like) systems, at configure time, the pango generation elements
are searched by pkg-config in the gdk-2.0 library. If this library or
pkg_config are not found, the rendering of non latin characters with
pango will be disabled, but this rendering may be done by other means,
especially by CMap (see the file chinese.abc). Note also that, when
pango is defined, it may be disabled at command line level by
'--pango 0'.
abcm2ps-8.5.2/Makefile0000644000175000017500000000741612462105220012611 0ustar  jefjef# Makefile source for abcm2ps

VERSION = 8.5.2

CC = gcc
INSTALL = /usr/bin/install -c
INSTALL_DATA = ${INSTALL} -m 644
INSTALL_PROGRAM = ${INSTALL}

CPPFLAGS = -DHAVE_PANGO=1 -I.
CPPPANGO = -I/usr/include/pango-1.0 -pthread -I/usr/include/cairo -I/usr/include/glib-2.0 -I/usr/lib/arm-linux-gnueabihf/glib-2.0/include -I/usr/include/pixman-1  -I/usr/include/libpng12 -I/usr/include/freetype2  
CFLAGS = -g -O2 -Wall -pipe
LDFLAGS =  -lpangocairo-1.0 -lcairo -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lfontconfig -lfreetype   -lm

prefix = /usr/local
exec_prefix = ${prefix}

srcdir = .
VPATH = .
bindir = ${exec_prefix}/bin
libdir = ${exec_prefix}/lib
datadir = ${prefix}/share
docdir = ${prefix}/doc

# unix
OBJECTS=abc2ps.o \
	abcparse.o buffer.o deco.o draw.o format.o front.o glyph.o music.o parse.o \
	slre.o subs.o svg.o syms.o
abcm2ps: $(OBJECTS)
	$(CC) $(CFLAGS) -o $@ $(OBJECTS) $(LDFLAGS)

$(OBJECTS): abcparse.h config.h Makefile
abcparse.o abc2ps.o buffer.o deco.o draw.o format.o front.o glyph.o \
	music.o parse.o subs.o svg.o syms.o: abc2ps.h
front.o parse.o slre.o: slre.h
subs.o: subs.c
	$(CC) $(CFLAGS) $(CPPFLAGS) $(CPPPANGO) -c -o $@ $<

DOCFILES=$(addprefix $(srcdir)/,Changes README *.abc *.eps *.txt)

install: abcm2ps
	mkdir -p $(bindir); \
	mkdir -p $(datadir)/abcm2ps; \
	mkdir -p $(docdir)/abcm2ps; \
	$(INSTALL_PROGRAM) abcm2ps $(bindir)
	for f in $(srcdir)/*.fmt; do \
		$(INSTALL_DATA) $$f $(datadir)/abcm2ps; \
	done
	for f in $(DOCFILES); do \
		$(INSTALL_DATA) $$f $(docdir)/abcm2ps; \
	done

uninstall:
	echo "uninstalling..."; \
	rm -f $(bindir)/abcm2ps; \
	rm -rf $(datadir)/abcm2ps; \
	rm -rf $(docdir)/abcm2ps

DIST_FILES = \
	abcm2ps-$(VERSION)/Changes \
	abcm2ps-$(VERSION)/INSTALL \
	abcm2ps-$(VERSION)/Makefile \
	abcm2ps-$(VERSION)/Makefile.in \
	abcm2ps-$(VERSION)/README \
	abcm2ps-$(VERSION)/abc2ps.c \
	abcm2ps-$(VERSION)/abc2ps.h \
	abcm2ps-$(VERSION)/abcparse.c \
	abcm2ps-$(VERSION)/abcparse.h \
	abcm2ps-$(VERSION)/accordion.abc \
	abcm2ps-$(VERSION)/build.ninja \
	abcm2ps-$(VERSION)/buffer.c \
	abcm2ps-$(VERSION)/chinese.abc \
	abcm2ps-$(VERSION)/configure \
	abcm2ps-$(VERSION)/config.h \
	abcm2ps-$(VERSION)/config.h.in \
	abcm2ps-$(VERSION)/deco.c \
	abcm2ps-$(VERSION)/deco.abc \
	abcm2ps-$(VERSION)/draw.c \
	abcm2ps-$(VERSION)/flute.fmt \
	abcm2ps-$(VERSION)/format.c \
	abcm2ps-$(VERSION)/front.c \
	abcm2ps-$(VERSION)/glyph.c \
	abcm2ps-$(VERSION)/glyphs.abc \
	abcm2ps-$(VERSION)/landscape.fmt \
	abcm2ps-$(VERSION)/music.c \
	abcm2ps-$(VERSION)/musicfont.fmt \
	abcm2ps-$(VERSION)/newfeatures.abc \
	abcm2ps-$(VERSION)/options.txt \
	abcm2ps-$(VERSION)/parse.c \
	abcm2ps-$(VERSION)/sample.abc \
	abcm2ps-$(VERSION)/sample2.abc \
	abcm2ps-$(VERSION)/sample3.abc \
	abcm2ps-$(VERSION)/sample3.eps \
	abcm2ps-$(VERSION)/sample4.abc \
	abcm2ps-$(VERSION)/sample5.abc \
	abcm2ps-$(VERSION)/sample8.html \
	abcm2ps-$(VERSION)/slre.c \
	abcm2ps-$(VERSION)/slre.h \
	abcm2ps-$(VERSION)/subs.c \
	abcm2ps-$(VERSION)/svg.c \
	abcm2ps-$(VERSION)/syms.c \
	abcm2ps-$(VERSION)/voices.abc

dist: Changes
	ln -s . abcm2ps-$(VERSION); \
	tar -zcvf abcm2ps-$(VERSION).tar.gz $(DIST_FILES); \
	rm abcm2ps-$(VERSION)

zip-dist:
	ln -s . abcm2ps-$(VERSION); \
	zip -r abcm2ps-$(VERSION).zip $(DIST_FILES); \
	rm abcm2ps-$(VERSION)

zip: abcm2ps.exe
	strip abcm2ps.exe; \
	cd ..; zip -r abcm2ps-$(VERSION).zip \
	abcm2ps-$(VERSION)/abcm2ps.exe \
	abcm2ps-$(VERSION)/License \
	abcm2ps-$(VERSION)/Changes \
	abcm2ps-$(VERSION)/INSTALL \
	abcm2ps-$(VERSION)/sample3.eps \
	abcm2ps-$(VERSION)/*.abc \
	abcm2ps-$(VERSION)/*.fmt \
	abcm2ps-$(VERSION)/*.txt ; cd -

EXAMPLES = accordion.ps \
	chinese.ps \
	deco.ps \
	newfeatures.ps \
	sample.ps \
	sample2.ps \
	sample3.ps \
	sample4.ps \
	sample5.ps \
	voices.ps

test:	$(EXAMPLES)
%.ps: %.abc
	./abcm2ps -O $@ $<

clean:
	rm -f *.o $(EXAMPLES) # *.obj
abcm2ps-8.5.2/Makefile.in0000644000175000017500000000672612452431030013221 0ustar  jefjef# Makefile source for abcm2ps

VERSION = @VERSION@

CC = @CC@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@

CPPFLAGS = @CPPFLAGS@ -I.
CPPPANGO = @CPPPANGO@
CFLAGS = @CFLAGS@
LDFLAGS = @LDFLAGS@ -lm

prefix = @prefix@
exec_prefix = @exec_prefix@

srcdir = @srcdir@
VPATH = @srcdir@
bindir = @bindir@
libdir = @libdir@
datadir = @datarootdir@
docdir = @docdir@

# unix
OBJECTS=abc2ps.o \
	abcparse.o buffer.o deco.o draw.o format.o front.o glyph.o music.o parse.o \
	slre.o subs.o svg.o syms.o
abcm2ps: $(OBJECTS)
	$(CC) $(CFLAGS) -o $@ $(OBJECTS) $(LDFLAGS)

$(OBJECTS): abcparse.h config.h Makefile
abcparse.o abc2ps.o buffer.o deco.o draw.o format.o front.o glyph.o \
	music.o parse.o subs.o svg.o syms.o: abc2ps.h
front.o parse.o slre.o: slre.h
subs.o: subs.c
	$(CC) $(CFLAGS) $(CPPFLAGS) $(CPPPANGO) -c -o $@ $<

DOCFILES=$(addprefix $(srcdir)/,Changes README *.abc *.eps *.txt)

install: abcm2ps
	mkdir -p $(bindir); \
	mkdir -p $(datadir)/abcm2ps; \
	mkdir -p $(docdir)/abcm2ps; \
	$(INSTALL_PROGRAM) abcm2ps $(bindir)
	for f in $(srcdir)/*.fmt; do \
		$(INSTALL_DATA) $$f $(datadir)/abcm2ps; \
	done
	for f in $(DOCFILES); do \
		$(INSTALL_DATA) $$f $(docdir)/abcm2ps; \
	done

uninstall:
	echo "uninstalling..."; \
	rm -f $(bindir)/abcm2ps; \
	rm -rf $(datadir)/abcm2ps; \
	rm -rf $(docdir)/abcm2ps

DIST_FILES = \
	abcm2ps-$(VERSION)/Changes \
	abcm2ps-$(VERSION)/INSTALL \
	abcm2ps-$(VERSION)/Makefile \
	abcm2ps-$(VERSION)/Makefile.in \
	abcm2ps-$(VERSION)/README \
	abcm2ps-$(VERSION)/abc2ps.c \
	abcm2ps-$(VERSION)/abc2ps.h \
	abcm2ps-$(VERSION)/abcparse.c \
	abcm2ps-$(VERSION)/abcparse.h \
	abcm2ps-$(VERSION)/accordion.abc \
	abcm2ps-$(VERSION)/build.ninja \
	abcm2ps-$(VERSION)/buffer.c \
	abcm2ps-$(VERSION)/chinese.abc \
	abcm2ps-$(VERSION)/configure \
	abcm2ps-$(VERSION)/config.h \
	abcm2ps-$(VERSION)/config.h.in \
	abcm2ps-$(VERSION)/deco.c \
	abcm2ps-$(VERSION)/deco.abc \
	abcm2ps-$(VERSION)/draw.c \
	abcm2ps-$(VERSION)/flute.fmt \
	abcm2ps-$(VERSION)/format.c \
	abcm2ps-$(VERSION)/front.c \
	abcm2ps-$(VERSION)/glyph.c \
	abcm2ps-$(VERSION)/glyphs.abc \
	abcm2ps-$(VERSION)/landscape.fmt \
	abcm2ps-$(VERSION)/music.c \
	abcm2ps-$(VERSION)/musicfont.fmt \
	abcm2ps-$(VERSION)/newfeatures.abc \
	abcm2ps-$(VERSION)/options.txt \
	abcm2ps-$(VERSION)/parse.c \
	abcm2ps-$(VERSION)/sample.abc \
	abcm2ps-$(VERSION)/sample2.abc \
	abcm2ps-$(VERSION)/sample3.abc \
	abcm2ps-$(VERSION)/sample3.eps \
	abcm2ps-$(VERSION)/sample4.abc \
	abcm2ps-$(VERSION)/sample5.abc \
	abcm2ps-$(VERSION)/sample8.html \
	abcm2ps-$(VERSION)/slre.c \
	abcm2ps-$(VERSION)/slre.h \
	abcm2ps-$(VERSION)/subs.c \
	abcm2ps-$(VERSION)/svg.c \
	abcm2ps-$(VERSION)/syms.c \
	abcm2ps-$(VERSION)/voices.abc

dist: Changes
	ln -s . abcm2ps-$(VERSION); \
	tar -zcvf abcm2ps-$(VERSION).tar.gz $(DIST_FILES); \
	rm abcm2ps-$(VERSION)

zip-dist:
	ln -s . abcm2ps-$(VERSION); \
	zip -r abcm2ps-$(VERSION).zip $(DIST_FILES); \
	rm abcm2ps-$(VERSION)

zip: abcm2ps.exe
	strip abcm2ps.exe; \
	cd ..; zip -r abcm2ps-$(VERSION).zip \
	abcm2ps-$(VERSION)/abcm2ps.exe \
	abcm2ps-$(VERSION)/License \
	abcm2ps-$(VERSION)/Changes \
	abcm2ps-$(VERSION)/INSTALL \
	abcm2ps-$(VERSION)/sample3.eps \
	abcm2ps-$(VERSION)/*.abc \
	abcm2ps-$(VERSION)/*.fmt \
	abcm2ps-$(VERSION)/*.txt ; cd -

EXAMPLES = accordion.ps \
	chinese.ps \
	deco.ps \
	newfeatures.ps \
	sample.ps \
	sample2.ps \
	sample3.ps \
	sample4.ps \
	sample5.ps \
	voices.ps

test:	$(EXAMPLES)
%.ps: %.abc
	./abcm2ps -O $@ $<

clean:
	rm -f *.o $(EXAMPLES) # *.obj
abcm2ps-8.5.2/README0000644000175000017500000000337212444060727012042 0ustar  jefjef		--- abcm2ps version 8.x.x ---

Overview
========

abcm2ps is a program which converts music tunes from ABC format to
PostScript or SVG. Based on abc2ps version 1.2.5 (see Contacts below),
it was developped mainly to print barock organ scores which have
independant voices played on one or many keyboards and a pedal-board
(the 'm' of abcm2ps stands for many or multi staves/voices).


Features
========

The main features of abcm2ps are quite the same as the abc2ps ones,
but they are closer to the ABC standard 2.1.1 (February 2013):

	http://abcnotation.com/wiki/abc:standard:v2.1.1


Installation and usage
======================

The installation procedure is described in the file INSTALL.

Basically, the program usage is:

   abcm2ps [options] file1 [file1_options] file2 [file2_options] ...

where file1, file2, .. are the ABC input files. This will generate
a Postscript file (default name: 'Out.ps' - run 'abcm2ps -h' to
know the list of the command line options).


Documentation
=============

- options.txt contains the list of the command line options.

- the format parameters are described in:
	http://moinejf.free.fr/abcm2ps-doc/index.html

- the differences from the current ABC standard are described in:
	http://moinejf.free.fr/abcm2ps-doc/index.html


Limits
======

Many limits may be changed at compilation time (number of voices,
staves, ..).


Contacts
========

The primary abcm2ps site is:

	http://moinejf.free.fr/

Guido Gonzatto maintains some abcm2ps binaries and more documentation at:

	http://abcplus.sourceforge.net/

abc2ps was developped by Michael Methfessel:

	http://www.ihp-ffo.de/~msm/
	mailto:msm@ihp-ffo.de

To know more about the ABC notation, have a look at:

	http://abcnotation.com/

For any comment:
	mailto:moinejf (at) free (dot) fr
abcm2ps-8.5.2/abc2ps.c0000644000175000017500000005724412454444705012512 0ustar  jefjef/*
 * abcm2ps: a program to typeset tunes written in ABC format
 *	using PostScript or SVG
 *
 * Copyright (C) 1998-2015 Jean-François Moine (http://moinejf.free.fr)
 *
 * Adapted from abc2ps-1.2.5:
 *  Copyright (C) 1996,1997  Michael Methfessel (msm@ihp-ffo.de)
 *
 * 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.
 */

#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>

#include "abc2ps.h"

#ifdef HAVE_MMAP
#include <unistd.h>
#include <sys/mman.h>
#elif defined(linux)
#include <unistd.h>
#endif

/* -- global variables -- */

INFO info;
struct SYMBOL *sym;		/* (points to the symbols of the current voice) */

int tunenum;			/* number of current tune */
int pagenum = 1;		/* current page in output file */

				/* switches modified by command line flags: */
int quiet;			/* quiet mode */
int secure;			/* secure mode */
int annotate;			/* output source references */
int pagenumbers;		/* write page numbers */
int epsf;			/* 1: EPSF, 2: SVG, 3: embedded ABC */
int svg;			/* 1: SVG, 2: XHTML */
int showerror;			/* show the errors */

char outfn[FILENAME_MAX];	/* output file name */
int file_initialized;		/* for output file */
FILE *fout;			/* output file */
char *in_fname;			/* current input file name */
time_t mtime;			/* last modification time of the input file */
static time_t fmtime;		/*	"	"	of all files */

int s_argc;			/* command line arguments */
char **s_argv;

struct tblt_s *tblts[MAXTBLT];
struct cmdtblt_s cmdtblts[MAXCMDTBLT];
int ncmdtblt;

/* -- local variables -- */

static char *styd = DEFAULT_FDIR; /* format search directory */
static int def_fmt_done = 0;	/* default format read */
static struct SYMBOL notitle;

/* memory arena (for clrarena, lvlarena & getarena) */
#define MAXAREAL 3		/* max area levels:
				 * 0; global, 1: tune, 2: generation */
#define AREANASZ 8192		/* standard allocation size */
#define MAXAREANASZ 0x20000	/* biggest allocation size */
static int str_level;		/* current arena level */
static struct str_a {
	struct str_a *n;	/* next area */
	char	*p;		/* pointer in area */
	int	r;		/* remaining space in area */
	int	sz;		/* size of str[] */
	char	str[2];		/* start of memory area */
} *str_r[MAXAREAL], *str_c[MAXAREAL];	/* root and current area pointers */

/* -- local functions -- */
static void read_def_format(void);
static void treat_file(char *fn, char *ext);

static FILE *open_ext(char *fn, char *ext)
{
	FILE *fp;
	char *p;

	if ((fp = fopen(fn, "rb")) != NULL)
		return fp;
	if ((p = strrchr(fn, DIRSEP)) == NULL)
		p = fn;
	if (strrchr(p, '.') != NULL)
		return NULL;
	strcat(p, ".");
	strcat(p, ext);
	if ((fp = fopen(fn, "rb")) != NULL)
		return fp;
	return NULL;
}

/* -- open a file for reading -- */
FILE *open_file(char *fn,	/* file name */
		char *ext,	/* file type */
		char *rfn)	/* returned real file name */
{
	FILE *fp;
	char *p;
	int l;

	/* if there was some ABC file, try its directory */
	if (in_fname && in_fname != fn
	 && (p = strrchr(in_fname, DIRSEP)) != NULL) {
		l = p - in_fname + 1;
		strncpy(rfn, in_fname, l);
		strcpy(&rfn[l], fn);
		if ((fp = open_ext(rfn, ext)) != NULL)
			return fp;
	}

	/* try locally */
	strcpy(rfn, fn);
	if ((fp = open_ext(rfn, ext)) != NULL)
		return fp;

	/* try a format in the format directory */
	if (*ext != 'f' || *styd == '\0')
		return NULL;
	l = strlen(styd) - 1;
	if (styd[l] == DIRSEP)
		sprintf(rfn, "%s%s", styd, fn);
	else
		sprintf(rfn, "%s%c%s", styd, DIRSEP, fn);
	return open_ext(rfn, ext);
}

/* -- read a whole input file -- */
/* the real/full file name is put in tex_buf[] */
static char *read_file(char *fn, char *ext)
{
	size_t fsize;
	FILE *fin;
	char *file;

	if (*fn == '\0') {
		strcpy(tex_buf, "stdin");
		fsize = 0;
		file = malloc(8192);
		for (;;) {
			int l;

			l = fread(&file[fsize], 1, 8192, stdin);
			fsize += l;
			if (l != 8192)
				break;
			file = realloc(file, fsize + 8192);
		}
		if (ferror(stdin) != 0) {
			free(file);
			return 0;
		}
		if (fsize % 8192 == 0)
			file = realloc(file, fsize + 2);
		time(&fmtime);
	} else {
		struct stat sbuf;

		fin = open_file(fn, ext, tex_buf);
		if (!fin)
			return NULL;
		if (fseek(fin, 0L, SEEK_END) < 0) {
			fclose(fin);
			return NULL;
		}
		fsize = ftell(fin);
		rewind(fin);
		if ((file = malloc(fsize + 2)) == NULL) {
			fclose(fin);
			return NULL;
		}

		if (fread(file, 1, fsize, fin) != fsize) {
			fclose(fin);
			free(file);
			return NULL;
		}
		fstat(fileno(fin), &sbuf);
		memcpy(&fmtime, &sbuf.st_mtime, sizeof fmtime);
		fclose(fin);
	}
	file[fsize] = '\0';
	return file;
}

/* call back to handle %%format/%%abc-include - see front.c */
void include_file(unsigned char *fn)
{
	static int nbfiles;

	if (nbfiles > 2) {
		error(1, NULL, "Too many included files");
		return;
	}

	nbfiles++;
	treat_file((char *) fn, "fmt");
	nbfiles--;
}

/* -- treat an input file and generate the ABC file -- */
static void treat_file(char *fn, char *ext)
{
	char *file;
	char *abc_fn;
	int file_type, l;

	/* initialize if not already done */
	if (!fout)
		read_def_format();

	/* read the file into memory */
	/* the real/full file name is in tex_buf[] */
	if ((file = read_file(fn, ext)) == NULL) {
		if (strcmp(fn, "default.fmt") != 0) {
			error(1, NULL, "Cannot read the input file '%s'", fn);
#if defined(unix) || defined(__unix__)
			perror("    read_file");
#endif
		}
		return;
	}
	abc_fn = strdup(tex_buf);
	if (!quiet)
		fprintf(stderr, "File %s\n", abc_fn);

	/* convert the strings */
	l = strlen(abc_fn);
	if (strcmp(&abc_fn[l - 3], ".ps") == 0) {
		file_type = FE_PS;
		frontend((unsigned char *) "%%beginps\n", FE_ABC,
				abc_fn, 0);
	} else if (strcmp(&abc_fn[l - 4], ".fmt") == 0) {
		file_type = FE_FMT;
	} else {
		file_type = FE_ABC;
		in_fname = abc_fn;
		mtime = fmtime;
	}

	frontend((unsigned char *) file, file_type,
				abc_fn, 0);
	free(file);

	if (file_type == FE_PS)			/* PostScript file */
		frontend((unsigned char *) "%%endps", FE_ABC,
				abc_fn, 0);
	if (file_type == FE_ABC)	/* if ABC file */
		clrarena(1);		/* clear previous tunes */
}

/* -- treat an ABC input file and generate the music -- */
/* this function also treats ABC in XHTML */
static void treat_abc_file(char *fn)
{
	FILE *fin;
	char *file, *file_tmp;
	char *abc_fn, *p, *q;
	size_t fsize, l, l2;
	int linenum;
#ifdef HAVE_MMAP
	int fd;
#endif

	lvlarena(0);
	parse.abc_state = ABC_S_GLOBAL;

	if (epsf != 3) {
		treat_file(fn, "abc");		/* not '-z' */
		return;
	}

	if (*fn == '\0') {
		error(1, NULL, "cannot use stdin with -z - aborting");
		exit(EXIT_FAILURE);
	}

	fin = open_file(fn, "abc", tex_buf);
	if (!fin)
		goto err;
	if (fseek(fin, 0L, SEEK_END) < 0) {
		fclose(fin);
		goto err;
	}
	fsize = ftell(fin);
	rewind(fin);
#ifdef HAVE_MMAP
	fd = fileno(fin);
	file = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, 0);
	if (!file)
		goto err;
#else
	file = malloc(fsize);
	if (!file)
		goto err;
	if (fread(file, 1, fsize, fin) != fsize) {
		free(file);
		goto err;
	}
	fclose(fin);
#endif

	/* copy the HTML/XML/XHTML file and generate the music */
	abc_fn = strdup(tex_buf);
	l = fsize;
	p = file;
	linenum = 0;
	while (l > 0) {

		/* search the start of ABC lines */
#if 1
		for (q = p, l2 = l - 10; l2 > 0; l2--, q++) {
			if (strncmp(q, "\n%abc2", 6) == 0
//			 || strncmp(q, "\n%%", 3) == 0
			 || strncmp(q, "\nX:", 3) == 0)
				break;
		}
#else
		for (q = p, l2 = l - 5; l2 > 0; l2--, q++)
			if (strncmp(q, "<abc>", 5) == 0)
				break;
#endif
		if (l2 <= 0) {
			fwrite(p, 1, l, fout);
			break;
		}
		q++;
		fwrite(p, 1, q - p, fout);
		l -= q - p;
		while (p != q) {
			if (*p++ == '\n')
				linenum++;
		}

		/* search the end of ABC lines */
		for (q = p, l2 = l - 10; l2 > 0; l2--, q++)
			if (*q == '\n' && q[1] == '<')
				break;
		if (l2 <= 0) {
			error(1, NULL, "no end of ABC sequence");
			q += 9;
//			break;
		}
		q++;

		/* must copy ... :( */
		l2 = q - p;
		file_tmp = malloc(l2 + 1);
		if (!file_tmp) {
			error(1, NULL, "out of memory");
			break;
		}
		memcpy(file_tmp, p, l2);
		file_tmp[l2] = '\0';

		frontend((unsigned char *) file_tmp, FE_ABC,
						abc_fn, linenum);
		free(file_tmp);

		clrarena(1);			/* clear previous tunes */
		file_initialized = -1;	/* don't put <br/> before first image */

		l -= q - p;
		while (p != q) {
			if (*p++ == '\n')
				linenum++;
		}
	}

#ifdef HAVE_MMAP
	munmap(file, fsize);
	fclose(fin);
#else
	free(file);
#endif
	return;
err:
	error(1, NULL, "input file %s error %s - aborting", fn, strerror(errno));
	exit(EXIT_FAILURE);
}

/* -- read the default format -- */
static void read_def_format(void)
{
	if (def_fmt_done)
		return;
	def_fmt_done = 1;
	treat_file("default.fmt", "fmt");
}

/* -- set extension on a file name -- */
void strext(char *fn, char *ext)
{
	char *p, *q;

	if ((p = strrchr(fn, DIRSEP)) == NULL)
		p = fn;
	if ((q = strrchr(p, '.')) == NULL)
		strcat(p, ".");
	else
		q[1] = '\0';
	strcat(p, ext);
}

/* -- write the program version -- */
static void display_version(int full)
{
	fputs("abcm2ps-" VERSION " (" VDATE ")\n", stderr);
	if (!full)
		return;
	fputs("Compiled: " __DATE__ "\n"
	       "Options:"
#ifdef A4_FORMAT
		" A4_FORMAT"
#endif
#ifdef DECO_IS_ROLL
		" DECO_IS_ROLL"
#endif
#ifdef HAVE_PANGO
		" PANGO"
#endif
#if !defined(A4_FORMAT) && !defined(DECO_IS_ROLL) && !defined(HAVE_PANGO)
		" NONE"
#endif
		"\n", stderr);
	if (styd[0] != '\0')
		fprintf(stderr, "Default format directory: %s\n", styd);
}

/* -- display usage and exit -- */
static void usage(void)
{
	display_version(0);
	printf(	"ABC to Postscript translator.\n"
		"Usage: abcm2ps [options] file [file_options] ..\n"
		"where:\n"
		" file        input ABC file, or '-'\n"
		" options and file_options:\n"
		"  .output file options:\n"
		"     -E      produce EPSF output, one tune per file\n"
		"     -g      produce SVG output, one tune per file\n"
		"     -v      produce SVG output, one page per file\n"
		"     -X      produce SVG output in one XHTML file\n"
		"     -z      produce SVG output from embedded ABC\n"
		"     -O fff  set outfile name to fff\n"
		"     -O =    make outfile name from infile/title\n"
		"     -i      indicate where are the errors\n"
		"     -k kk   size of the PS output buffer in Kibytes\n"
		"  .output formatting:\n"
		"     -s xx   set scale factor to xx\n"
		"     -w xx   set staff width (cm/in/pt)\n"
		"     -m xx   set left margin (cm/in/pt)\n"
		"     -d xx   set staff separation (cm/in/pt)\n"
		"     -a xx   set max shrinkage to xx (between 0 and 1)\n"
		"     -F foo  read format file \"foo.fmt\"\n"
		"     -D bar  look for format files in directory \"bar\"\n"
		"  .output options:\n"
		"     -l      landscape mode\n"
		"     -I xx   indent 1st line (cm/in/pt)\n"
		"     -x      add xref numbers in titles\n"
		"     -M      don't output the lyrics\n"
		"     -N n    set page numbering mode to n=\n"
		"             0=off 1=left 2=right 3=even left,odd right 4=even right,odd left\n"
		"     -1      write one tune per page\n"
		"     -G      no slur in grace notes\n"
		"     -j n[b] number the measures every n bars (or on the left if n=0)\n"
		"             if 'b', display in a box\n"
		"     -b n    set the first measure number to n\n"
		"     -f      have flat beams\n"
		"     -T n[v]   output the tablature 'n' for voice 'v' / all voices\n"
		"  .line breaks:\n"
		"     -c      auto line break\n"
		"     -B n    break every n bars\n"
		"  .input file selection/options:\n"
		"     -e pattern\n"
		"             tune selection\n"
		"  .help/configuration:\n"
		"     -V      show program version\n"
		"     -h      show this command summary\n"
		"     -H      show the format parameters\n"
		"     -S      secure mode\n"
		"     -q      quiet mode\n");
	exit(EXIT_SUCCESS);
}

#ifdef linux
/* -- where is the default format directory -- */
static void wherefmtdir(void)
{
	char exe[512], *p;
	FILE *f;
	int l;

	if ((l = readlink("/proc/self/exe", exe, sizeof exe)) <= 0)
		return;
	if ((p = strrchr(exe, '/')) == NULL)
		return;
	p++;
	if (p > &exe[5] && strncmp(p - 5, "/bin", 4) == 0) {
		strcpy(p - 4, "share/abcm2ps/");
		p += -4 + 14;
	}
	/* else, assume this is the source directory */

	/* check if a format file is present */
	strcpy(p, "tight.fmt");
	if ((f = fopen(exe, "r")) == NULL)
		return;
	fclose(f);

	/* change the format directory */
	p[-1] = '\0';
	styd = strdup(exe);
}
#endif

/* -- parse the tablature command ('-T n[v]') -- */
static struct cmdtblt_s *cmdtblt_parse(char *p)
{
	struct cmdtblt_s *cmdtblt;
	short val;

	if (ncmdtblt >= MAXCMDTBLT) {
		error(1, NULL, "++++ Too many '-T'");
		return NULL;
	}
	if (*p == '\0')
		val = -1;
	else {
		val = *p++ - '0' - 1;
		if ((unsigned) val > MAXTBLT) {
			error(1, NULL, "++++ Bad tablature number in '-T'\n");
			return 0;
		}
	}
	cmdtblt = &cmdtblts[ncmdtblt++];
	cmdtblt->index = val;
	cmdtblt->vn = p;
	return cmdtblt;
}

/* set a command line option */
static void set_opt(char *w, char *v)
{
	static char prefix = '%';	/* pseudo-comment prefix */

	if (!v)
		v = "";
	if (strlen(w) + strlen(v) >= TEX_BUF_SZ - 10) {
		error(1, NULL, "Command line '%s' option too long", w);
		return;
	}
	sprintf(tex_buf,		/* this buffer is available */
		"%%%c%s %s lock\n", prefix, w, v);
	if (strcmp(w, "abcm2ps") == 0)
		prefix = *v;
	frontend((unsigned char *) tex_buf, FE_ABC,
			"cmd_line", 0);
}

/* -- main program -- */
int main(int argc, char **argv)
{
	unsigned j;
	char *p, c, *aaa;

	if (argc <= 1)
		usage();

	outfn[0] = '\0';
	init_outbuf(0);

	/* set the global flags */
	s_argc = argc;
	s_argv = argv;
	aaa = NULL;
	while (--argc > 0) {
		argv++;
		p = *argv;
		if (*p != '-' || p[1] == '-') {
			if (*p == '+' && p[1] == 'F')	/* +F : no default format */
				def_fmt_done = 1;
			continue;
		}
		while ((c = *++p) != '\0') {	/* '-xxx' */
			switch (c) {
			case 'E':
				svg = 0;	/* EPS */
				epsf = 1;
				break;
			case 'g':
				svg = 0;	/* SVG one file per tune */
				epsf = 2;
				break;
			case 'h':
				usage();	/* no return */
			case 'q':
				quiet = 1;
				break;
			case 'S':
				secure = 1;
				break;
			case 'V':
				display_version(1);
				return EXIT_SUCCESS;
			case 'v':
				svg = 1;	/* SVG one file per pagee */
				epsf = 0;
				break;
			case 'X':
				svg = 2;	/* SVG/XHTML */
				epsf = 0;
				break;
			case 'k': {
				int kbsz;

				if (p[1] == '\0') {
					if (--argc <= 0) {
						error(1, NULL, "No value for '-k' - aborting");
						return EXIT_FAILURE;
					}
					aaa = *++argv;
				} else {
					aaa = p + 1;
					p += strlen(p) - 1;
				}

				sscanf(aaa, "%d", &kbsz);
				init_outbuf(kbsz);
				break;
			    }
			case 'O':
				if (p[1] == '\0') {
					if (--argc <= 0) {
						error(1, NULL, "No value for '-O' - aborting");
						return EXIT_FAILURE;
					}
					aaa = *++argv;
				} else {
					aaa = p + 1;
					p += strlen(p) - 1;
				}
				if (strlen(aaa) >= sizeof outfn) {
					error(1, NULL, "'-O' too large - aborting");
					return EXIT_FAILURE;
				}
				strcpy(outfn, aaa);
				break;
			case 'z':
				epsf = 3;	/* ABC embedded in XML */
				svg = 0;
				break;
			default:
				if (strchr("aBbDdeFfIjmNOsTw", c)) /* if with arg */
					p += strlen(p) - 1;	/* skip */
				break;
			}
		}
	}
	if (!quiet)
		display_version(0);

	/* initialize */
	clrarena(0);				/* global */
	clrarena(1);				/* tunes */
	clrarena(2);				/* generation */
//	memset(&info, 0, sizeof info);
	info['T' - 'A'] = &notitle;
	notitle.as.text = "T:";
	set_format();
	init_deco();

#ifdef linux
	/* if not set, try to find where is the default format directory */
	if (styd[0] == '\0')
		wherefmtdir();
#endif
#ifdef HAVE_PANGO
	pg_init();
#endif

	/* if ABC embedded in XML, open the output file */
	if (epsf == 3) {
		open_fout();
		read_def_format();
	}

	/* parse the arguments - finding a new file, treat the previous one */
	argc = s_argc;
	argv = s_argv;
	while (--argc > 0) {
		argv++;
		p = *argv;
		if ((c = *p) == '\0')
			continue;
		if (c == '-') {
			int i;

			if (p[1] == '\0') {		/* '-' alone */
				if (in_fname) {
					treat_abc_file(in_fname);
					frontend((unsigned char *) "select\n", FE_ABC,
							"cmd_line", 0);
				}
				in_fname = "";		/* read from stdin */
				continue;
			}
			i = strlen(p) - 1;
			if (p[i] == '-'
			 && p[1] != '-'
//fixme: 'e' may be preceded by other options
			 && p[1] != 'e'
			 && p[i -1] != 'O')
				c = '+'; /* switch off flags with '-x-' */
		}
		if (c == '+') {		/* switch off flags with '+' */
			while (*++p != '\0') {
				switch (*p) {
				case '-':
					break;
				case 'B':
					cfmt.barsperstaff = 0;
					lock_fmt(&cfmt.barsperstaff);
					break;
				case 'c':
					cfmt.continueall = 0;
					lock_fmt(&cfmt.continueall);
					break;
				case 'F':
//					 def_fmt_done = 1;
					break;
				case 'G':
					cfmt.graceslurs = 1;
					lock_fmt(&cfmt.graceslurs);
					break;
				case 'i':
					showerror = 0;
					break;
				case 'j':
					cfmt.measurenb = -1;
					lock_fmt(&cfmt.measurenb);
					break;
				case 'l':
					cfmt.landscape = 0;
					lock_fmt(&cfmt.landscape);
					break;
				case 'M':
					cfmt.fields[1] = 1 << ('w' - 'a');
					lock_fmt(&cfmt.fields);
					break;
				case 'N':
					pagenumbers = 0;
					break;
				case 'O':
					outfn[0] = '\0';
					break;
				case 'T': {
					struct cmdtblt_s *cmdtblt;

					aaa = p + 1;
					if (*aaa == '\0') {
						if (argc > 1
						    && argv[1][0] != '-') {
							aaa = *++argv;
							argc--;
						}
					} else {
						while (p[1] != '\0')	/* stop */
							p++;
						if (*p == '-')
							*p-- = '\0';	/* (not clean) */
					}
					cmdtblt = cmdtblt_parse(aaa);
					if (cmdtblt != 0)
						cmdtblt->active = 0;
					break;
				   }
				case 'x':
					cfmt.fields[0] &= ~(1 << ('X' - 'A'));
					lock_fmt(&cfmt.fields);
					break;
				case '0':
					cfmt.splittune = 0;
					lock_fmt(&cfmt.splittune);
					break;
				case '1':
					cfmt.oneperpage = 0;
					lock_fmt(&cfmt.oneperpage);
					break;
				default:
					error(1, NULL,
						"++++ Cannot switch off flag: +%c",
						*p);
					break;
				}
			}
			continue;
		}

		if (c == '-') {		     /* interpret a flag with '-' */
			if (p[1] == '-') {		/* long argument */
				p += 2;
				if (--argc <= 0) {
					error(1, NULL, "No argument for '--'");
					return EXIT_FAILURE;
				}
				argv++;
				set_opt(p, *argv);
				continue;
			}
			while ((c = *++p) != '\0') {
				switch (c) {

					/* simple flags */
				case 'A':
					annotate = 1;
					break;
				case 'c':
					cfmt.continueall = 1;
					lock_fmt(&cfmt.continueall);
					break;
				case 'E':
					break;
				case 'f':
					cfmt.flatbeams = 1;
					lock_fmt(&cfmt.flatbeams);
					break;
				case 'G':
					cfmt.graceslurs = 0;
					lock_fmt(&cfmt.graceslurs);
					break;
				case 'g':
					break;
				case 'H':
					if (!fout) {
						read_def_format();
						make_font_list();
					}
					print_format();
					return EXIT_SUCCESS;
				case 'i':
					showerror = 1;
					break;
				case 'l':
					cfmt.landscape = 1;
					lock_fmt(&cfmt.landscape);
					break;
				case 'M':
					cfmt.fields[1] &= ~(1 << ('w' - 'a'));
					lock_fmt(&cfmt.fields);
					break;
				case 'q':
				case 'S':
					break;
				case 'v':
				case 'X':
				case 'z':
					break;
				case 'x':
					cfmt.fields[0] |= 1 << ('X' - 'A');
					lock_fmt(&cfmt.fields);
					break;
				case '0':
					cfmt.splittune = 1;
					lock_fmt(&cfmt.splittune);
					break;
				case '1':
					cfmt.oneperpage = 1;
					lock_fmt(&cfmt.oneperpage);
					break;

					/* flag with optional parameter */
				case 'N':
					if (p[1] == '\0'
					 && (argc <= 1
					  || !isdigit((unsigned) argv[1][0]))) {
						pagenumbers = 2; /* old behaviour */
						break;
					}
					/* fall thru */
					/* flags with parameter.. */
				case 'a':
				case 'B':
				case 'b':
				case 'D':
				case 'd':
				case 'e':
				case 'F':
				case 'I':
				case 'j':
				case 'k':
				case 'L':
				case 'm':
				case 'O':
				case 's':
				case 'T':
				case 'w':
					aaa = p + 1;
					if (*aaa == '\0') {
						aaa = *++argv;
						if (--argc <= 0
						 || (*aaa == '-' && c != 'O')) {
							error(1, NULL,
								"Missing parameter after '-%c' - aborting",
								c);
							return EXIT_FAILURE;
						}
					} else {
						p += strlen(p) - 1;	/* stop */
					}

					if (strchr("BbfjkNs", c)) {	/* check num args */
						for (j = 0; j < strlen(aaa); j++) {
							if (!strchr("0123456789.",
								    aaa[j])) {
								if (aaa[j] == 'b'
								 && aaa[j + 1] == '\0'
								 && c == 'j')
									break;
								error(1, NULL,
									"Invalid parameter <%s> for flag -%c",
									aaa, c);
								return EXIT_FAILURE;
							}
						}
					}

					switch (c) {
					case 'a':
						set_opt("maxshrink", aaa);
						break;
					case 'B':
						set_opt("barsperstaff", aaa);
						break;
					case 'b':
						set_opt("measurefirst", aaa);
						break;
					case 'D':
						styd = aaa;
						break;
					case 'd':
						set_opt("staffsep", aaa);
						break;
					case 'e':
						set_opt("select", aaa);
						break;
					case 'F':
						treat_file(aaa, "fmt");
						break;
					case 'I':
						set_opt("indent", aaa);
						break;
					case 'j':
						sscanf(aaa, "%d", &cfmt.measurenb);
						lock_fmt(&cfmt.measurenb);
						if (aaa[strlen(aaa) - 1] == 'b')
							cfmt.measurebox = 1;
						else
							cfmt.measurebox = 0;
						lock_fmt(&cfmt.measurebox);
						break;
					case 'k':
						break;
					case 'm':
						set_opt("leftmargin", aaa);
						break;
					case 'N':
						sscanf(aaa, "%d", &pagenumbers);
						if ((unsigned) pagenumbers > 4) {
							error(1, NULL,
								"'-N' value %s - changed to 2",
								aaa);
							pagenumbers = 2;
						}
						break;
					case 'O':
						if (strlen(aaa) >= sizeof outfn) {
							error(1, NULL, "'-O' too large - aborting");
							exit(EXIT_FAILURE);
						}
						strcpy(outfn, aaa);
						break;
					case 's':
						set_opt("scale", aaa);
						break;
					case 'T': {
						struct cmdtblt_s *cmdtblt;

						cmdtblt = cmdtblt_parse(aaa);
						if (cmdtblt)
							cmdtblt->active = 1;
						break;
					    }
					case 'w':
						set_opt("staffwidth", aaa);
						break;
					}
					break;
				default:
					error(1, NULL,
						"Unknown flag: -%c ignored", c);
					break;
				}
			}
			continue;
		}

		if (in_fname) {
			treat_abc_file(in_fname);
			frontend((unsigned char *) "select\n", FE_ABC,
						"cmd_line", 0);
		}
		in_fname = p;
	}

	if (in_fname)
		treat_abc_file(in_fname);
	if (multicol_start != 0) {		/* lack of %%multicol end */
		error(1, NULL, "Lack of %%%%multicol end");
		multicol_start = 0;
		buffer_eob();
		if (!info['X' - 'A']
		 && !epsf)
			write_buffer();
	}
	if (!epsf && !fout) {
		error(1, NULL, "Nothing to generate!");
		return EXIT_FAILURE;
	}
	close_output_file();
	return severity == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

/* -- arena routines -- */
void clrarena(int level)
{
	struct str_a *a_p;

	if ((a_p = str_r[level]) == NULL) {
		str_r[level] = a_p = malloc(sizeof *str_r[0] + AREANASZ - 2);
		a_p->sz = AREANASZ;
		a_p->n = 0;
	}
	str_c[level] = a_p;
	a_p->p = a_p->str;
	a_p->r = sizeof a_p->str;
}

int lvlarena(int level)
{
	int old_level;

	old_level = str_level;
	str_level = level;
	return old_level;
}

/* The area is 8 bytes aligned to handle correctly int and pointers access
 * on some machines as Sun Sparc. */
void *getarena(int len)
{
	char *p;
	struct str_a *a_p;

	a_p = str_c[str_level];
	len = (len + 7) & ~7;		/* align at 64 bits boundary */
	if (len > a_p->r) {
		if (len > MAXAREANASZ) {
			error(1, NULL,
				"getarena - data too wide %d - aborting",
				len);
			exit(EXIT_FAILURE);
		}
		if (len > AREANASZ) {			/* big allocation */
			struct str_a *a_n;

			a_n = a_p->n;
			a_p->n = malloc(sizeof *str_r[0] + len - 2);
			a_p->n->n = a_n;
			a_p->n->sz = len;
		} else if (a_p->n == 0) {		/* standard allocation */
			a_p->n = malloc(sizeof *str_r[0] + AREANASZ - 2);
			a_p->n->n = 0;
			a_p->n->sz = AREANASZ;
		}
		str_c[str_level] = a_p = a_p->n;
		a_p->p = a_p->str;
		a_p->r = a_p->sz;
	}
	p = a_p->p;
	a_p->p += len;
	a_p->r -= len;
	return p;
}
abcm2ps-8.5.2/abc2ps.h0000644000175000017500000005161712461140656012511 0ustar  jefjef/* -- general macros -- */

#include <stdio.h>
#include <time.h>

#include "config.h"
#include "abcparse.h"

#define OUTPUTFILE	"Out.ps"	/* standard output file */
#ifndef WIN32
#define DIRSEP '/'
#else
#define DIRSEP '\\'
#endif

#define CM		* 28.35	/* factor to transform cm to pt */
#define PT			/* factor to transform pt to pt */
#define IN		* 72.0	/* factor to transform inch to pt */

/* basic page dimensions */
#ifdef A4_FORMAT
#define PAGEHEIGHT	(29.7 CM)
#define PAGEWIDTH	(21.0 CM)
#define MARGIN		(1.8 CM)
#else
#define PAGEHEIGHT	(11.0 IN)
#define PAGEWIDTH	(8.5 IN)
#define MARGIN		(0.7 IN)
#endif

/* -- macros controlling music typesetting -- */

#define STEM_YOFF	1.0	/* offset stem from note center */
#define STEM_XOFF	3.5
#define STEM		21	/* default stem height = one octave */
#define STEM_MIN	16	/* min stem height under beams */
#define STEM_MIN2	14	/* ... for notes with two beams */
#define STEM_MIN3	12	/* ... for notes with three beams */
#define STEM_MIN4	10	/* ... for notes with four beams */
#define STEM_CH_MIN	14	/* min stem height for chords under beams */
#define STEM_CH_MIN2	10	/* ... for notes with two beams */
#define STEM_CH_MIN3	 9	/* ... for notes with three beams */
#define STEM_CH_MIN4	 9	/* ... for notes with four beams */
#define BEAM_DEPTH	3.2	/* width of a beam stroke */
#define BEAM_OFFSET	0.25	/* pos of flat beam relative to staff line */
#define BEAM_SHIFT	5.0	/* shift of second and third beams */
/*  To align the 4th beam as the 1st: shift=6-(depth-2*offset)/3  */
#define BEAM_FLATFAC	0.6	/* factor to decrease slope of long beams */
#define BEAM_THRESH	0.06	/* flat beam if slope below this threshold */
#define BEAM_SLOPE	0.5	/* max slope of a beam */
#define BEAM_STUB	6.0	/* length of stub for flag under beam */ 
#define SLUR_SLOPE	1.0	/* max slope of a slur */
#define GSTEM		14	/* grace note stem length */
#define GSTEM_XOFF	1.6	/* x offset for grace note stem */

#define BETA_C		0.1	/* max expansion for flag -c */
#define BETA_X		1.0	/* max expansion before complaining */

#define VOCPRE		0.4	/* portion of vocals word before note */
#define GCHPRE		0.4	/* portion of guitar chord before note */

/* -- Parameters for note spacing -- */
/* fnn multiplies the spacing under a beam, to compress the notes a bit */

#define fnnp 0.9

/* -- macros for program internals -- */

#define STRL1		256	/* string length for file names */
#define MAXSTAFF	16	/* max staves */
#define BSIZE		512	/* buffer size for one input string */

#define BREVE		(BASE_LEN * 2)	/* double note (square note) */
#define SEMIBREVE	BASE_LEN	/* whole note */
#define MINIM		(BASE_LEN / 2)	/* half note (white note) */
#define CROTCHET 	(BASE_LEN / 4)	/* quarter note (black note) */
#define QUAVER		(BASE_LEN / 8)	/* 1/8 note */
#define SEMIQUAVER	(BASE_LEN / 16)	/* 1/16 note */

#define MAXFONTS	30	/* max number of fonts */

#define T_LEFT		0
#define T_JUSTIFY	1
#define T_FILL		2
#define T_CENTER	3
#define T_SKIP		4
#define T_RIGHT		5

#define YSTEP	128		/* number of steps for y offsets */

extern char *deco[256];

struct FONTSPEC {
	int fnum;		/* index to font tables in format.c */
	float size;
	float swfac;
};
extern char *fontnames[MAXFONTS];	/* list of font names */

/* lyrics */
#define LY_HYPH	0x10	/* replacement character for hyphen */
#define LY_UNDER 0x11	/* replacement character for underscore */
#define MAXLY	16	/* max number of lyrics */
struct lyl {
	struct FONTSPEC *f;	/* font */
	float w;		/* width */
	float s;		/* shift / note */
	char t[1];		/* word */
};
struct lyrics {
	struct lyl *lyl[MAXLY];	/* ptr to lyric lines */
};

/* guitar chord / annotations */
#define MAXGCH 8		/* max number of guitar chords / annotations */
struct gch {
	char type;		/* ann. char, 'g' gchord, 'r' repeat, '\0' end */
	unsigned char idx;	/* index in as.text */
	unsigned char font;	/* font */
	char box;		/* 1 if in box */
	float x, y;		/* x y offset / note + (top or bottom) of staff */
	float w;		/* width */
};

/* positions / directions */
/* 0: auto, 1: above/up (SL_ABOVE), 2: below/down (SL_BELOW)
 * 3: hidden (SL_AUTO) or opposite for gstemdir */
#define SL_HIDDEN SL_AUTO
struct posit_s {
	unsigned short dyn:2;	/* %%dynamic */
	unsigned short gch:2;	/* %%gchord */
	unsigned short orn:2;	/* %%ornament */
	unsigned short voc:2;	/* %%vocal */
	unsigned short vol:2;	/* %%volume */
	unsigned short std:2;	/* %%stemdir */
	unsigned short gsd:2;	/* %%gstemdir */
};

/* music element */
struct SYMBOL { 		/* struct for a drawable symbol */
	struct abcsym as;	/* abc symbol !!must be the first field!! */
	struct SYMBOL *next, *prev;	/* voice linkage */
	struct SYMBOL *ts_next, *ts_prev; /* time linkage */
	struct SYMBOL *extra;	/* extra symbols (grace notes, tempo... */
	unsigned char type;	/* symbol type */
#define NO_TYPE		0	/* invalid type */
#define NOTEREST	1	/* valid symbol types */
#define SPACE		2
#define BAR		3
#define CLEF		4
#define TIMESIG 	5
#define KEYSIG		6
#define TEMPO		7
#define STAVES		8
#define MREST		9
#define PART		10
#define GRACE		11
#define FMTCHG		12
#define TUPLET		13
#define STBRK		14
#define CUSTOS		15
#define NSYMTYPES	16
	unsigned char voice;	/* voice (0..nvoice) */
	unsigned char staff;	/* staff (0..nstaff) */
	unsigned char nhd;	/* number of notes in chord - 1 */
	int dur;		/* main note duration */
	signed char pits[MAXHD]; /* pitches for notes
				  * pits[0] = y base for KEYSIG */
	int time;		/* starting time */
	unsigned int sflags;	/* symbol flags */
#define S_EOLN		0x0001		/* end of line */
#define S_BEAM_ST	0x0002		/* beam starts here */
#define S_BEAM_BR1	0x0004		/* 2nd beam must restart here */
#define S_BEAM_BR2	0x0008		/* 3rd beam must restart here */
#define S_BEAM_END	0x0010		/* beam ends here */
//#define S_free	0x0020
#define S_IN_TUPLET	0x0040		/* in a tuplet */
#define S_TREM2		0x0080		/* tremolo on 2 notes */
#define S_RRBAR		0x0100		/* right repeat bar (when bar) */
#define S_XSTEM		0x0200		/* cross-staff stem (when note) */
#define S_BEAM_ON	0x0400		/* continue beaming */
#define S_SL1		0x0800		/* some chord slur start */
#define S_SL2		0x1000		/* some chord slur end */
#define S_TI1		0x2000		/* some chord tie start */
#define S_PERC		0x4000		/* percussion */
#define S_RBSTOP	0x8000		/* repeat bracket stop */
#define S_FEATHERED_BEAM 0x00010000	/* feathered beam */
#define S_REPEAT	0x00020000	/* sequence / measure repeat */
#define S_NL		0x00040000	/* start of new music line */
#define S_SEQST		0x00080000	/* start of vertical sequence */
#define S_SECOND	0x00100000	/* symbol on a secondary voice */
#define S_FLOATING	0x00200000	/* symbol on a floating voice */
#define S_NOREPBRA	0x00400000	/* don't print the repeat bracket */
#define S_TREM1		0x00800000	/* tremolo on 1 note */
#define S_TEMP		0x01000000	/* temporary symbol */
#define S_SHIFTUNISON_1	0x02000000	/* %%shiftunison 1 */
#define S_SHIFTUNISON_2	0x04000000	/* %%shiftunison 2 */
#define S_NEW_SY	0x08000000	/* staff system change (%%staves) */
#define S_CLEF_AUTO	0x10000000	/* auto clef (when clef) */
	struct posit_s posit;	/* positions / directions */
	signed char stem;	/* 1 / -1 for stem up / down */
	signed char nflags;	/* number of note flags when > 0 */
	char dots;		/* number of dots */
	unsigned char head;	/* head type */
#define H_FULL		0
#define H_EMPTY 	1
#define H_OVAL		2
#define H_SQUARE	3
	signed char multi;	/* multi voice in the staff (+1, 0, -1) */
	signed char nohdi1;	/* no head index (for unison) / nb of repeat */
	signed char nohdi2;
	signed char doty;	/* NOTEREST: y pos of dot when voices overlap
				 * STBRK: forced
				 * FMTCHG REPEAT: infos */
	short u;		/* auxillary information:
				 *	- CLEF: small clef
				 *	- KEYSIG: old key signature
				 *	- BAR: new bar number
				 *	- TUPLET: tuplet format
				 *	- NOTE: tremolo number / feathered beam
				 *	- FMTCHG (format change): subtype */
#define PSSEQ 0				/* postscript sequence */
#define SVGSEQ 1			/* SVG sequence */
#define REPEAT 2			/* repeat sequence or measure
					 *	doty: # measures if > 0
					 *	      # notes/rests if < 0
					 *	nohdi1: # repeat */
#define STAFF_COLOR 3			/* color change: */
#define VOICE_COLOR 4			/*	color in as.u.length.base_length */
	float x;		/* x offset */
	signed char y;		/* y offset of note head */
	signed char ymn, ymx, yav; /* min, max, avg note head y offset */
	float xmx;		/* max h-pos of a head rel to top
				 * width when STBRK */
	float xs, ys;		/* coord of stem end / bar height */
	float wl, wr;		/* left, right min width */
	float space;		/* natural space before symbol */
	float shrink;		/* minimum space before symbol */
	float xmax;		/* max x offset */
	float shhd[MAXHD];	/* horizontal shift for heads */
	float shac[MAXHD];	/* horizontal shift for accidentals */
	struct gch *gch;	/* guitar chords / annotations */
	struct lyrics *ly;	/* lyrics */
};

/* bar types !tied to abcparse.h! */
#define B_SINGLE B_BAR		/* |	single bar */
#define B_DOUBLE 0x11		/* ||	thin double bar */
#define B_THIN_THICK 0x13	/* |]	thick at section end  */
#define B_THICK_THIN 0x21	/* [|	thick at section start */
#define B_LREP 0x14		/* |:	left repeat bar */
#define B_RREP 0x41		/* :|	right repeat bar */
#define B_DREP 0x44		/* ::	double repeat bar */
#define B_DASH 0x04		/* :	dashed bar */

#define	FONT_UMAX 10		/* max number of user fonts 0..9 */
enum e_fonts {
	ANNOTATIONFONT = FONT_UMAX,
	COMPOSERFONT,
	FOOTERFONT,
	GCHORDFONT,
	HEADERFONT,
	HISTORYFONT,
	INFOFONT,
	MEASUREFONT,
	PARTSFONT,
	REPEATFONT,
	SUBTITLEFONT,
	TEMPOFONT,
	TEXTFONT,
	TITLEFONT,
	VOCALFONT,
	VOICEFONT,
	WORDSFONT,
	FONT_DYN		/* index of dynamic fonts (gch, an, ly) */
};
#define	FONT_DYNX 12				/* number of dynamic fonts */
#define	FONT_MAX (FONT_DYN + FONT_DYNX)		/* whole number of fonts */

struct FORMAT { 		/* struct for page layout */
	float pageheight, pagewidth;
	float topmargin, botmargin, leftmargin, rightmargin;
	float topspace, wordsspace, titlespace, subtitlespace, partsspace;
	float composerspace, musicspace, vocalspace, textspace;
	float breaklimit, maxshrink, lineskipfac, parskipfac, stemheight;
	float indent, infospace, slurheight, notespacingfactor, scale;
	float staffsep, sysstaffsep, maxstaffsep, maxsysstaffsep, stretchlast;
	int abc2pscompat, alignbars, aligncomposer, autoclef;
	int barsperstaff, breakoneoln, bstemdown, cancelkey;
	int combinevoices, contbarnb, continueall, custos;
	int dblrepbar, dynalign, flatbeams;
	int infoline, gchordbox, graceslurs, gracespace, hyphencont;
	int keywarn, landscape, linewarn;
	int measurebox, measurefirst, measurenb, micronewps;
	int oneperpage;
#ifdef HAVE_PANGO
	int pango;
#endif
	int partsbox, pdfmark;
	int setdefl, shiftunison, splittune, squarebreve;
	int staffnonote, straightflags, stretchstaff;
	int textoption, titlecaps, titleleft, titletrim;
	int timewarn, transpose, tuplets;
	char *bgcolor, *dateformat, *header, *footer, *titleformat;
	struct FONTSPEC font_tb[FONT_MAX];
	char ndfont;		/* current index of dynamic fonts */
	unsigned char gcf, anf, vof;	/* fonts for guitar chords,
					 * annotations and lyrics */
	unsigned int fields[2];	/* info fields to print
				 *[0] is 'A'..'Z', [1] is 'a'..'z' */
	struct posit_s posit;
};

extern struct FORMAT cfmt;	/* current format */
extern struct FORMAT dfmt;	/* global format */

typedef struct SYMBOL *INFO[26]; /* information fields ('A' .. 'Z') */
extern INFO info;

extern char *outbuf;		/* output buffer.. should hold one tune */
extern char *mbf;		/* where to PUTx() */
extern int use_buffer;		/* 1 if lines are being accumulated */

extern int outft;		/* last font in the output file */
extern int tunenum;		/* number of current tune */
extern int pagenum;		/* current page number */
extern int nbar;		/* current measure number */
extern int in_page;
extern int defl;		/* decoration flags */
#define DEF_NOST 0x01		/* long deco with no start */
#define DEF_NOEN 0x02		/* long deco with no end */
#define DEF_STEMUP 0x04		/* stem up (1) or down (0) */

		/* switches modified by flags: */
extern int quiet;		/* quiet mode */
extern int secure;		/* secure mode */
extern int annotate;		/* output source references */
extern int pagenumbers; 	/* write page numbers */
extern int epsf;		/* 1: EPSF, 2: SVG, 3: embedded ABC */
extern int svg;			/* 1: SVG, 2: XHTML */
extern int showerror;		/* show the errors */

extern char outfn[FILENAME_MAX]; /* output file name */
extern char *in_fname;		/* current input file name */
extern time_t mtime;		/* last modification time of the input file */

extern int file_initialized;	/* for output file */
extern FILE *fout;		/* output file */

#define MAXTBLT 8
struct tblt_s {
	char *head;		/* PS head function */
	char *note;		/* PS note function */
	char *bar;		/* PS bar function */
	float wh;		/* width of head */
	float ha;		/* height above the staff */
	float hu;		/* height under the staff */
	short pitch;		/* pitch when no associated 'w:' / 0 */
	char instr[2];		/* instrument pitch */
};
extern struct tblt_s *tblts[MAXTBLT];

#define MAXCMDTBLT	4	/* max number of -T in command line */
struct cmdtblt_s {
	short index;		/* tablature number */
	short active;		/* activate or not */
	char *vn;		/* voice name */
};
extern struct cmdtblt_s cmdtblts[MAXCMDTBLT];
extern int ncmdtblt;

extern int s_argc;		/* command line arguments */
extern char **s_argv;

struct STAFF_S {
	struct SYMBOL *s_clef;	/* clef at start of music line */
	char empty;		/* no symbol on this staff */
	signed char stafflines;
	float staffscale;
	short botbar, topbar;	/* bottom and top of bar */
	float y;		/* y position */
	float top[YSTEP], bot[YSTEP];	/* top/bottom y offsets */
	int color;
};
extern struct STAFF_S staff_tb[MAXSTAFF];
extern int nstaff;		/* (0..MAXSTAFF-1) */

struct VOICE_S {
	char id[VOICE_ID_SZ];	/* voice id */
							/* generation */
	struct VOICE_S *next;	/* link */
	struct SYMBOL *sym;	/* associated symbols */
	struct SYMBOL *last_sym; /* last symbol while scanning */
	struct SYMBOL *lyric_start;	/* start of lyrics while scanning */
	char *nm;		/* voice name */
	char *snm;		/* voice subname */
	char *bar_text;		/* bar text at start of staff when bar_start */
	struct gch *bar_gch;	/* bar text */
	struct SYMBOL *tie;	/* note with ties of previous line */
	struct SYMBOL *rtie;	/* note with ties before 1st repeat bar */
	struct tblt_s *tblts[2]; /* tablatures */
	float scale;		/* scale */
	int time;		/* current time (parsing) */
	struct SYMBOL *s_clef;	/* clef at end of music line */
	struct key_s key;	/* current key signature */
	struct meter_s meter;	/* current time signature */
	struct key_s ckey;	/* key signature while parsing */
	struct key_s okey;	/* original key signature (parsing) */
	unsigned hy_st;		/* lyrics hyphens at start of line (bit array) */
	unsigned ignore:1;	/* ignore this voice (%%staves) */
	unsigned second:1;	/* secondary voice in a brace/parenthesis */
	unsigned floating:1;	/* floating voice in a brace system */
	unsigned bar_repeat:1;	/* bar at start of staff is a repeat bar */
	unsigned norepbra:1;	/* don't display the repeat brackets */
	unsigned have_ly:1;	/* some lyrics in this voice */
	unsigned new_name:1;	/* redisplay the voice name */
	unsigned space:1;	/* have a space before the next note (parsing) */
	unsigned perc:1;	/* percussion */
	unsigned auto_len:1;	/* auto L: (parsing) */
	short wmeasure;		/* measure duration (parsing) */
	short transpose;	/* transposition (parsing) */
	short bar_start;	/* bar type at start of staff / 0 */
	struct posit_s posit;	/* positions / directions */
	signed char octave;	/* octave (parsing) */
	signed char clone;	/* duplicate from this voice number */
	signed char over;	/* overlay of this voice number */
	unsigned char staff;	/* staff (0..n-1) */
	unsigned char cstaff;	/* staff (parsing) */
	unsigned char slur_st;	/* slurs at start of staff */
	signed char stafflines;
	int color;
	float staffscale;
							/* parsing */
	struct abcsym *last_note;	/* last note or rest */
	short ulen;			/* unit note length */
	unsigned char microscale;	/* microtone scale */
	unsigned char mvoice;		/* main voice when voice overlay */
};
extern struct VOICE_S voice_tb[MAXVOICE]; /* voice table */
extern struct VOICE_S *first_voice; /* first_voice */

extern struct SYMBOL *tsfirst;	/* first symbol in the time linked list */
extern struct SYMBOL *tsnext;	/* next line when cut */
extern float realwidth;		/* real staff width while generating */

#define NFLAGS_SZ 10		/* size of note flags tables */
#define C_XFLAGS 5		/* index of crotchet in flags tables */
extern float space_tb[NFLAGS_SZ]; /* note spacing */

struct SYSTEM {			/* staff system */
	struct SYSTEM *next;
	short top_voice;	/* first voice in the staff system */
	short nstaff;
	struct {
		short flags;
#define OPEN_BRACE 0x01
#define CLOSE_BRACE 0x02
#define OPEN_BRACKET 0x04
#define CLOSE_BRACKET 0x08
#define OPEN_PARENTH 0x10
#define CLOSE_PARENTH 0x20
#define STOP_BAR 0x40
#define FL_VOICE 0x80
#define OPEN_BRACE2 0x0100
#define CLOSE_BRACE2 0x0200
#define OPEN_BRACKET2 0x0400
#define CLOSE_BRACKET2 0x0800
#define MASTER_VOICE 0x1000
		char empty;
		signed char stafflines;
		float staffscale;
//		struct clef_s clef;
		float sep, maxsep;
	} staff[MAXSTAFF];
	struct {
		signed char range;
		unsigned char staff;
		char second;
		char dum;
		float sep, maxsep;
//		struct clef_s clef;
	} voice[MAXVOICE];
};
struct SYSTEM *cursys;		/* current staff system */

/* -- external routines -- */
/* abc2ps.c */
void include_file(unsigned char *fn);
void clrarena(int level);
int lvlarena(int level);
void *getarena(int len);
void strext(char *fid, char *ext);
/* buffer.c */
void a2b(char *fmt, ...)
#ifdef __GNUC__
	__attribute__ ((format (printf, 1, 2)))
#endif
	;
void block_put(void);
void buffer_eob(void);
void marg_init(void);
void bskip(float h);
void check_buffer(void);
void init_outbuf(int kbsz);
void close_output_file(void);
void close_page(void);
float get_bposy(void);
void open_fout(void);
void write_buffer(void);
int (*output)(FILE *out, const char *fmt, ...)
#ifdef __GNUC__
	__attribute__ ((format (printf, 2, 3)))
#endif
	;
void write_eps(void);
/* deco.c */
void deco_add(char *text);
void deco_cnv(struct decos *dc, struct SYMBOL *s, struct SYMBOL *prev);
void deco_update(struct SYMBOL *s, float dx);
float deco_width(struct SYMBOL *s);
void draw_all_deco(void);
int draw_deco_head(int deco, float x, float y, int stem);
void draw_deco_near(void);
void draw_deco_note(void);
void draw_deco_staff(void);
float draw_partempo(int staff, float top);
void draw_measnb(void);
void init_deco(void);
void reset_deco(void);
void set_defl(int new_defl);
float tempo_width(struct SYMBOL *s);
void write_tempo(struct SYMBOL *s,
		int beat,
		float sc);
float y_get(int staff,
		int up,
		float x,
		float w);
void y_set(int staff,
		int up,
		float x,
		float w,
		float y);
/* draw.c */
void draw_sym_near(void);
void draw_all_symb(void);
float draw_systems(float indent);
void output_ps(struct SYMBOL *s, int color);
void putf(float f);
void putx(float x);
void puty(float y);
void putxy(float x, float y);
void set_scale(struct SYMBOL *s);
void set_sscale(int staff);
void set_st_color(int st);
void set_v_color(int v);
/* format.c */
void define_fonts(void);
int get_textopt(char *p);
int get_font_encoding(int ft);
void interpret_fmt_line(char *w, char *p, int lock);
void lock_fmt(void *fmt);
void make_font_list(void);
FILE *open_file(char *fn,
		char *ext,
		char *rfn);
void print_format(void);
void set_font(int ft);
void set_format(void);
void set_voice_param(struct VOICE_S *p_voice, int state, char *w, char *p);
struct tblt_s *tblt_parse(char *p);
/* front.c */
#define FE_ABC 0
#define FE_FMT 1
#define FE_PS 2
void frontend(unsigned char *s,
		int ftype,
		char *fname,
		int linenum);
/* glyph.c */
char *glyph_out(char *p);
void glyph_add(char *p);
/* music.c */
void output_music(void);
void reset_gen(void);
void unlksym(struct SYMBOL *s);
/* parse.c */
extern float multicol_start;
void do_tune(void);
void identify_note(struct SYMBOL *s,
		int len,
		int *p_head,
		int *p_dots,
		int *p_flags);
void sort_pitch(struct SYMBOL *s, int combine);
struct SYMBOL *sym_add(struct VOICE_S *p_voice,
			int type);
/* subs.c */
void bug(char *msg, int fatal);
void error(int sev, struct SYMBOL *s, char *fmt, ...);
float scan_u(char *str);
float cwid(unsigned char c);
void get_str_font(int *cft, int *dft);
void set_str_font(int cft, int dft);
#ifdef HAVE_PANGO
void pg_init(void);
void pg_reset_font(void);
#endif
void put_history(void);
void put_words(struct SYMBOL *words);
void str_font(int ft);
#define A_LEFT 0
#define A_CENTER 1
#define A_RIGHT 2
#define A_LYRIC 3
#define A_GCHORD 4
#define A_ANNOT 5
#define A_GCHEXP 6
void str_out(char *p, int action);
void put_str(char *str, int action);
float tex_str(char *s);
extern char tex_buf[];	/* result of tex_str() */
#define TEX_BUF_SZ 512
char *trim_title(char *p, struct SYMBOL *title);
void user_ps_add(char *s, char use);
void user_ps_write(void);
void write_title(struct SYMBOL *s);
void write_heading(void);
void write_user_ps(void);
void write_text(char *cmd, char *s, int job);
/* svg.c */
void define_svg_symbols(char *title, int num, float w, float h);
int svg_output(FILE *out, const char *fmt, ...)
#ifdef __GNUC__
	__attribute__ ((format (printf, 2, 3)))
#endif
	;
void svg_write(char *buf, int len);
void svg_close();
/* syms.c */
void define_font(char *name, int num, int enc);
void define_symbols(void);
abcm2ps-8.5.2/abcparse.c0000644000175000017500000015202612461506650013106 0ustar  jefjef/*
 * Generic ABC parser.
 *
 * Copyright (C) 1998-2015 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.
 */

#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "abc2ps.h"

/* global values */
int severity;			/* error severity */

static short ulen;		/* unit note length set by M: or L: */
static short meter;		/* upper value of time sig for n-plets */
static unsigned char microscale; /* current microtone scale */
static signed char vover;	/* voice overlay (1: single bar, -1: multi-bar */
static char lyric_started;	/* lyric started */
static char *gchord;		/* guitar chord */
static struct decos dc;		/* decorations */
static struct abcsym *deco_start; /* 1st note of the line for d: / s: */
static struct abcsym *deco_cont; /* current symbol when d: / s: continuation */

static int g_abc_vers, g_ulen, g_microscale;
static char g_char_tb[128];
static char *g_deco_tb[128];		/* global decoration names */
static unsigned short g_micro_tb[MAXMICRO]; /* global microtone values */

#define VOICE_NAME_SZ 64	/* max size of a voice name */

static char *abc_fn;		/* current source file name */
static int linenum;		/* current source line number */
static int colnum;		/* current source column number */
static char *abc_line;		/* line being parsed */
static struct abcsym *last_sym;	/* last symbol for errors */

static short nvoice;		/* number of voices (0..n-1) */
static struct VOICE_S *curvoice;

/* char table for note line parsing */
#define CHAR_BAD 0
#define CHAR_IGN 1
#define CHAR_NOTE 2
#define CHAR_GR_ST 3
#define CHAR_DECO 4
#define CHAR_GCHORD 5
#define CHAR_BSLASH 6
#define CHAR_OBRA 7
#define CHAR_BAR 8
#define CHAR_OPAR 9
#define CHAR_VOV 10
#define CHAR_SPAC 11
#define CHAR_MINUS 12
#define CHAR_CPAR 13
#define CHAR_BRHY 14
#define CHAR_DECOS 15
#define CHAR_SLASH 16
#define CHAR_GR_EN 17
#define CHAR_LINEBREAK 18
static char char_tb[256] = {
	0, 0, 0, 0, 0, 0, 0, 0,				/* 00 - 07 */
	0, CHAR_SPAC, CHAR_LINEBREAK, 0, 0, 0, 0, 0,	/* 08 - 0f */
	0, 0, 0, 0, 0, 0, 0, 0,				/* 10 - 17 */
	0, 0, 0, 0, 0, 0, 0, 0,				/* 18 - 1f */
	CHAR_SPAC, CHAR_DECOS, CHAR_GCHORD, CHAR_BAD,	/* (sp) ! " # */
	CHAR_BAD, CHAR_BAD, CHAR_VOV, CHAR_BAD, 	/* $ % & ' */
	CHAR_OPAR, CHAR_CPAR, CHAR_BAD, CHAR_DECOS, 	/* ( ) * + */
	CHAR_BAD, CHAR_MINUS, CHAR_DECO, CHAR_SLASH, 	/* , - . / */
	CHAR_BAD, CHAR_BAD, CHAR_BAD, CHAR_BAD, 	/* 0 1 2 3 */
	CHAR_BAD, CHAR_BAD, CHAR_BAD, CHAR_BAD, 	/* 4 5 6 7 */
	CHAR_BAD, CHAR_BAD, CHAR_BAR, CHAR_BAD, 	/* 8 9 : ; */
	CHAR_BRHY, CHAR_NOTE, CHAR_BRHY, CHAR_BAD, 	/* < = > ? */
	CHAR_BAD, CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, 	/* @ A B C */
	CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, 	/* D E F G */
	CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, 	/* H I J K */
	CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, 	/* L M N O */
	CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, 	/* P Q R S */
	CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, 	/* T U V W */
	CHAR_NOTE, CHAR_DECO, CHAR_NOTE, CHAR_OBRA, 	/* X Y Z [ */
	CHAR_BSLASH, CHAR_BAR, CHAR_NOTE, CHAR_NOTE, 	/* \ ] ^ _ */
	CHAR_IGN, CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, 	/* ` a b c */
	CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, 	/* d e f g */
	CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, 	/* h i j k */
	CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, 	/* l m n o */
	CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, 	/* p q r s */
	CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, 	/* t u v w */
	CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, CHAR_GR_ST, 	/* x y z { */
	CHAR_BAR, CHAR_GR_EN, CHAR_DECO, CHAR_BAD, 	/* | } ~ (del) */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* 80 - 8f */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* 90 - 9f */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* a0 - af */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* b0 - bf */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* c0 - cf */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* d0 - df */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* e0 - ef */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/* f0 - ff */
};

static const char all_notes[] = "CDEFGABcdefgab";

static char *parse_len(char *p,
			int *p_len);
static int parse_info(char *p);
static char *parse_gchord(char *p);
static int parse_line(char *p);
static char *parse_note(char *p,
			int flags);
static void syntax(char *msg, char *q);
static void vover_new(void);

/* -- abcMIDI like errors -- */
static void print_error(char *s, int col)
{
	if (col >= 0)
		fprintf(stderr, "%s:%d:%d: error: %s\n", abc_fn, linenum, col, s);
	else
		fprintf(stderr, "%s:%d: error: %s\n", abc_fn, linenum, s);
}

/* -- new symbol -- */
static struct abcsym *abc_new(int type, char *text)
{
	struct abcsym *s;

	s = getarena(sizeof(struct SYMBOL));
	memset(s, 0, sizeof(struct SYMBOL));
	if (text) {
		s->text = getarena(strlen(text) + 1);
		strcpy(s->text, text);
	}
	if (!parse.last_sym) {
		parse.first_sym = s;
	} else {
		if ((s->next = parse.last_sym->next) != NULL)
			s->next->prev = s;
		parse.last_sym->next = s;
		s->prev = parse.last_sym;
	}
	last_sym = parse.last_sym = s;
	s->type = type;
	s->state = parse.abc_state;
	s->fn = abc_fn;
	s->linenum = linenum;
	s->colnum = colnum;
	return s;
}

/* -- parse an ABC line -- */
void abc_parse(char *p, char *fname, int ln)
{
	abc_fn = fname;
	linenum = ln;
	abc_line = p;

	/* parse the music line */
	switch (parse_line(p)) {
	case 2:				/* start of tune */
		g_abc_vers = parse.abc_vers;
		g_ulen = ulen;
		g_microscale = microscale;
		meter = 2;
		memcpy(g_char_tb, char_tb, sizeof g_char_tb);
		memcpy(g_deco_tb, parse.deco_tb, sizeof g_deco_tb);
		memcpy(g_micro_tb, parse.micro_tb, sizeof g_micro_tb);
		break;
	case 1:				/* end of tune */
		if (parse.first_sym) {
			do_tune();
			parse.first_sym = parse.last_sym = NULL;
		}
		parse.abc_state = ABC_S_GLOBAL;
		parse.abc_vers = g_abc_vers;
		ulen = g_ulen;
		microscale = g_microscale;
		memcpy(char_tb, g_char_tb, sizeof g_char_tb);
		memcpy(parse.deco_tb, g_deco_tb, sizeof parse.deco_tb);
		memcpy(parse.micro_tb, g_micro_tb, sizeof parse.micro_tb);
		lvlarena(0);
		if (dc.n > 0)
			syntax("Decoration without symbol", 0);
		dc.n = 0;
		break;
	}
}

/* treat the end of file */
void abc_eof(void)
{
	if (parse.abc_state == ABC_S_HEAD)
		severity = 1;
	do_tune();
	parse.first_sym = parse.last_sym = NULL;
	if (parse.abc_state != ABC_S_GLOBAL) {
		parse.abc_vers = g_abc_vers;
		ulen = g_ulen;
		microscale = g_microscale;
		memcpy(char_tb, g_char_tb, sizeof g_char_tb);
	}
}

/* -- treat the broken rhythm '>' and '<' -- */
static void broken_rhythm(struct note *note,
			  int num)	/* >0: do dot, <0: do half */
{
	int l, m, n;

	num *= 2;
	if (num > 0) {
		if (num == 6)
			num = 8;
		n = num * 2 - 1;
		for (m = 0; m <= note->nhd; m++)
			note->lens[m] = (note->lens[m] * n) / num;
	} else {
		n = -num;
		if (n == 6)
			n = 8;
		for (m = 0; m <= note->nhd; m++)
			note->lens[m] /= n;
	}
	l = note->lens[0];
	for (m = 1; m <= note->nhd; m++)
		if (note->lens[m] < l)
			l = note->lens[m];
}

/* -- check for the '!' as end of line (ABC2Win) -- */
static int check_nl(char *p)
{
	while (*p != '\0') {
		switch (*p++) {
		case '!':
			return 0;
		case '|':
		case '[':
		case ':':
		case ']':
		case ' ':
		case '\t':
			return 1;
		}
	}
	return 1;
}

/* -- parse extra K: or V: definitions (clef, octave and microscale  -- */
static char *parse_extra(char *p,
			char **p_name,
			char **p_middle,
			char **p_lines,
			char **p_scale,
			char **p_octave,
			char **p_cue)
{
	for (;;) {
		if (strncmp(p, "clef=", 5) == 0
		 || strncmp(p, "bass", 4) == 0
		 || strncmp(p, "treble", 6) == 0
		 || strncmp(p, "alto", 4) == 0
		 || strncmp(p, "tenor", 5) == 0
		 || strncmp(p, "perc", 4) == 0) {
			if (*p_name)
				syntax("Double clef name", p);
			*p_name = p;
		} else if (strncmp(p, "microscale=", 11) == 0) {
			int i;

			i = atoi(p + 11);
			if (i < 4 || i >= 256)
				syntax("Invalid value in microscale=", p);
			else
				microscale = i;
		} else if (strncmp(p, "middle=", 7) == 0
			|| strncmp(p, "m=", 2) == 0) {
			if (*p_middle)
				syntax("Double clef middle", p);
			*p_middle = p + (p[1] == '=' ? 2 : 7);
		} else if (strncmp(p, "octave=", 7) == 0) {
			if (*p_octave)
				syntax("Double octave=", p);
			*p_octave = p + 7;
		} else if (strncmp(p, "stafflines=", 11) == 0) {
			if (*p_lines)
				syntax("Double stafflines", p);
			*p_lines = p + 11;
		} else if (strncmp(p, "staffscale=", 11) == 0) {
			if (*p_scale)
				syntax("Double staffscale", p);
			*p_scale = p + 11;
		} else if (strncmp(p, "cue=", 4) == 0) {
			if (*p_cue)
				syntax("Double cue", p);
			*p_cue = p + 4;
		} else if (strncmp(p, "transpose=", 10) == 0
			|| strncmp(p, "t=", 2) == 0) {
			;		/* ignored */
		} else {
			break;
		}
		while (!isspace((unsigned char) *p) && *p != '\0')
			p++;
		while (isspace((unsigned char) *p))
			p++;
		if (*p == '\0')
			break;
	}
	return p;
}

/* -- parse a decoration 'xxx<decosep>' -- */
static char *get_deco(char *p,
		      unsigned char *p_dc)
{
	char *q, sep, **t;
	unsigned i, l;

	*p_dc = 0;
	q = p;
	sep = q[-1];
	if (char_tb[(unsigned char) sep] == CHAR_DECOS) {
		if (sep == '+') {
			if (*p == '+' && p[1] == '+')
				p++;		/* special case "+++" */
		}
	} else {
		sep = '\0';			/* Barfly U: */
	}
	while (*p != sep) {
		if (*p == '\0') {
			syntax("Decoration not terminated", q);
			return p;
		}
		p++;
	}
	l = p - q;
	if (*p == sep)
		p++;
	for (i = 1, t = &parse.deco_tb[1];
	     *t && i < DC_NAME_SZ;
	     i++, t++) {
		if (strlen(*t) == l
		 && strncmp(*t, q, l) == 0) {
			*p_dc = i + 128;
			return p;
		}
	}

	/* new decoration */
	if (i < DC_NAME_SZ) {
//		if (parse.abc_state != ABC_S_GLOBAL)
//			lvlarena(0);
		*t = getarena(l + 1);
//		if (parse.abc_state != ABC_S_GLOBAL)
//			lvlarena(1);
		memcpy(*t, q, l);
		(*t)[l] = '\0';
		*p_dc = i + 128;
	} else {
		syntax("Too many decoration types", q);
	}
	return p;
}

/* -- parse a list of accidentals (K:) -- */
static char *parse_acc(char *p,
			struct abcsym *s)
{
	int pit, len, acc, nostem;
	unsigned nacc;

	nacc = 0;
	for (;;) {
		if (nacc >= sizeof s->u.key.pits) {
			syntax("Too many accidentals", p);
			break;
		}
		p = parse_basic_note(p, &pit, &len, &acc, &nostem);
		s->u.key.pits[nacc] = pit;
		s->u.key.accs[nacc++] = acc;
		while (isspace((unsigned char) *p))
			p++;
		if (*p == '\0')
			break;
		if (*p != '^' && *p != '_' && *p != '=')
			break;
	}
	s->u.key.microscale = microscale;
	if (s->u.key.empty != 2)
		s->u.key.nacc = nacc;
	return p;
}

/* -- parse a clef (K: or V:) -- */
static void parse_clef(struct abcsym *s,
			char *name,
			char *middle)
{
	int clef = -1;
	int transpose = 0;
	int clef_line = 2;
	char *warn = NULL;
	char str[80];

	str[0] = '\0';
	if (name && strncmp(name, "clef=", 5) == 0) {
		name += 5;
		switch (*name) {
		case '\"':
			name = get_str(str, name, sizeof str);
			s->u.clef.name = getarena(strlen(str) + 1);
			strcpy(s->u.clef.name, str);
			clef = TREBLE;
			break;
		case 'g':
			warn = name;
			transpose = -7;
		case 'G':
			clef = TREBLE;
			break;
		case 'f':
			warn = name;
			transpose = -14;
			clef = BASS;
			clef_line = 4;
			break;
		case 'F':
			if (name[1] == ',')	/* abc2.1.1 clef=F == clef=F, */
				transpose = -7;
			clef = BASS;
			clef_line = 4;
			break;
		case 'c':
			warn = name;
			transpose = -7;
		case 'C':
			clef = ALTO;
			clef_line = 3;
			break;
		case 'P':
			clef = PERC;
			break;
		}
		if (clef >= 0) {
			name++;
			if (*name == ',' || *name== '\'')
				warn = name;
			while (*name == ',') {
				transpose += 7;
				name++;
			}
			while (*name == '\'') {
				transpose -= 7;
				name++;
			}
		}
	}
	if (name && clef < 0) {
		if (!strncmp(name, "bass", 4)) {
			clef = BASS;
			clef_line = 4;
			s->u.clef.check_pitch = 1;
			name += 4;
		} else if (!strncmp(name, "treble", 6)) {
			clef = TREBLE;
			name += 6;
		} else if (!strncmp(name, "alto", 4)
			|| !strncmp(name, "tenor", 5)) {
			clef = ALTO;
			clef_line = *name == 'a' ? 3 : 4;
			s->u.clef.check_pitch = 1;
			if (*name == 'a')
				name += 4;
			else
				name += 5;
		} else if (!strncmp(name, "perc", 4)) {
			clef = PERC;
			name += 4;
		} else if (!strncmp(name, "auto", 4)) {
			clef = AUTOCLEF;
			name += 4;
		} else if (strncmp(name, "none", 4) == 0) {
			clef = TREBLE;
			s->u.clef.invis = 1;
			s->flags |= ABC_F_INVIS;
			name += 4;
		} else {
			syntax("Unknown clef", name);
		}
	}

	if (clef >= 0) {
		switch (*name) {
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
			clef_line = *name++ - '0';
			break;
		}
		if (name[1] == '8') {
			switch (*name) {
			case '^':
				transpose -= 7;
			case '+':
				s->u.clef.octave = 1;
				break;
			case '_':
				transpose += 7;
			case '-':
				s->u.clef.octave = -1;
				break;
			}
		}
	}

	if (middle) {
		int pit, len, acc, nostem, l;
		static const char line_tb[7] =
			{ALTO, TREBLE, ALTO, BASS, ALTO, BASS, ALTO};

		warn = middle;
		/* 'middle=<note pitch>' */
		parse_basic_note(middle, &pit, &len, &acc, &nostem);

		if (clef < 0)
			clef = line_tb[(pit + 7) % 7];
	
		switch (clef) {
		default:
			l = 20 + 4;
			break;
		case ALTO:
			l = 16 + 4;
			break;
		case BASS:
			l = 12 + 4;
			break;
		}
		clef_line = (l - pit + 28) % 7;
		if (clef_line & 1) {
			syntax("Bad 'middle' value for the clef", middle);
			pit++;
		}
		clef_line = clef_line / 2 + 1;

		transpose = l - (clef_line - 1) * 2 - pit;
		s->u.clef.check_pitch = 0;
	}

	s->u.clef.type = clef;
	s->u.clef.line = clef_line;
	s->u.clef.transpose = transpose;
	if (warn) {
		int sev_sav;

		sev_sav = severity;
		syntax("Warning: Deprecated or non-standard item", warn);
		severity = sev_sav;
	}
}

/* get the octave= value */
static int parse_octave(char *p)
{
	int oct;

	if (p) {
		oct = 1;
		if (*p == '-') {
			oct = -1;
			p++;
		}
		if (*p >= '0' && *p <= '4')
			return oct * (*p - '0');
		syntax("Bad octave value", p);
	}
	return NO_OCTAVE;
}

/* -- parse a 'K:' -- */
static void parse_key(char *p,
		      struct abcsym *s)
{
	int sf, mode, empty;
	char *clef_name, *clef_middle, *clef_lines, *clef_scale;
	char *p_octave, *p_cue;

	// set important default values
	s->u.key.stafflines = -1;
	s->u.key.octave = NO_OCTAVE;

	if (*p == '\0') {
		s->u.key.empty = 1;
		return;
	}
	sf = 0;
	mode = MAJOR;
	empty = 0;
	switch (*p++) {
	case 'F': sf = -1; break;
	case 'B': sf++;
	case 'E': sf++;
	case 'A': sf++;
	case 'D': sf++;
	case 'G': sf++;
	case 'C': break;
	case 'H':
		if (*p == 'P') {
			mode = BAGPIPE;
			p++;
		} else if (*p == 'p') {
			mode = BAGPIPE + 1;
			sf = 2;
			p++;
		} else {
			syntax("Unknown bagpipe-like key", p);
		}
		break;
	case 'n':
		if (strncmp(p, "one", 3) == 0) {	// none
			empty = 2;
			p += 3;
			while (isspace((unsigned char) *p))
				p++;
			if (*p == '\0')
				return;
			break;
		}
		// fall thru
	default:
		p--;
		empty = 1;
		break;
	}

	s->u.key.empty = empty;
	if (!empty) {
		if (*p == '#') {
			sf += 7;
			p++;
		} else if (*p == 'b') {
			sf -= 7;
			p++;
		}
		while (isspace((unsigned char) *p))
			p++;
		switch (*p) {
		case 'a':
		case 'A':
			if (strncasecmp(p, "aeo", 3) == 0) {
				sf -= 3;
				mode = 5;
				break;
			}
			goto unk;
		case 'd':
		case 'D':
			if (strncasecmp(p, "dor", 3) == 0) {
				sf -= 2;
				mode = 1;
				break;
			}
			goto unk;
		case 'i':
		case 'I':
			if (strncasecmp(p, "ion", 3) == 0) {
				mode = 0;
				break;
			}
			goto unk;
		case 'l':
		case 'L':
			if (strncasecmp(p, "loc", 3) == 0) {
				sf -= 5;
				mode = 6;
				break;
			}
			if (strncasecmp(p, "lyd", 3) == 0) {
				sf += 1;
				mode = 3;
				break;
			}
			goto unk;
		case 'm':
		case 'M':
			if (strncasecmp(p, "maj", 3) == 0)
				break;
			if (strncasecmp(p, "mix", 3) == 0) {
				sf -= 1;
				mode = 4;
				break;
			}
			if (strncasecmp(p, "min", 3) == 0
			 || !isalpha((unsigned char) p[1])) { /* 'm' alone */
				sf -= 3;
				mode = MINOR;
				break;
			}
			goto unk;
		case 'p':
		case 'P':
			if (strncasecmp(p, "phr", 3) == 0) {
				sf -= 4;
				mode = 2;
				break;
			}
			goto unk;
		default:
unk:
			empty = 1;
			break;
		}
		if (!empty) {
			while (isalpha((unsigned char) *p))
				p++;
			while (isspace((unsigned char) *p))
				p++;
		}

		// [exp] accidentals
		if (strncmp(p, "exp ", 4) == 0) {
			p += 4;
			while (isspace((unsigned char) *p))
				p++;
			if (*p == '\0')
				syntax("no accidental after 'exp'", p);
			s->u.key.exp = 1;
		}
		if (s->u.key.exp && strncmp(p, "none", 4) == 0) {
			sf = 0;
			p += 4;
			while (isspace((unsigned char) *p))
				p++;
		} else switch (*p) {
			case '^':
			case '_':
			case '=':
				p = parse_acc(p, s);		/* accidentals */
				break;
		}
	}

	if (sf > 7 || sf < -7) {
		syntax("Too many sharps/flats", p);
		if (sf > 0)
			sf -= 12;
		else
			sf += 12;
	}

	// extra parameters
	clef_name = clef_middle = clef_lines = clef_scale = NULL;
	p_octave = p_cue = NULL;
	parse_extra(p, &clef_name, &clef_middle, &clef_lines,
			&clef_scale, &p_octave, &p_cue);

	s->u.key.sf = sf;
	s->u.key.mode = mode;
	s->u.key.octave = parse_octave(p_octave);
	if (p_cue) {
		if (strncmp(p_cue, "on", 2) == 0)
			s->u.key.cue = 1;
		else
			s->u.key.cue = -1;
	}
	if (clef_lines) {
		int l;

		l = atoi(clef_lines);
		if ((unsigned) l <= 12)
			s->u.key.stafflines = l;
		else
			syntax("Bad value of stafflines", clef_lines);
	}
	if (clef_scale) {
		float sc;

		sc = atof(clef_scale);
		if (sc >= 0.5 && sc <= 3)
			s->u.key.staffscale = sc;
		else
			syntax("Bad value of staffscale", clef_scale);
	}
	if (clef_name || clef_middle) {
		s = abc_new(ABC_T_CLEF, NULL);
		parse_clef(s, clef_name, clef_middle);
	}
}

/* -- set default length from 'L:' -- */
static char *get_len(char *p,
		     struct abcsym *s)
{
	int l1, l2, d;
	char *error_txt = NULL;

	if (strcmp(p, "auto") == 0) {		/* L:auto */
		s->u.length.base_length = -1;
		return error_txt;
	}
	l1 = 0;
	l2 = 1;
	if (sscanf(p, "%d /%d ", &l1, &l2) != 2
	 || l1 == 0) {
		s->u.length.base_length = ulen ? ulen : BASE_LEN / 8;
		return "Bad unit note length: unchanged";
	}

	if (l2 == 0) {
		error_txt = "Bad length divisor, set to 4";
		l2 = 4;
	}
	d = BASE_LEN / l2;
	if (d * l2 != BASE_LEN) {
		error_txt = "Length incompatible with BASE, using 1/8";
		d = BASE_LEN / 8;
	} else 	{
		d *= l1;
		if (l1 != 1
		 || (l2 & (l2 - 1))) {
			error_txt = "Incorrect unit note length, using 1/8";
			d = BASE_LEN / 8;
		}
	}
	s->u.length.base_length = d;
	return error_txt;
}

/* -- parse a 'M:' -- */
static char *parse_meter(char *p,
			 struct abcsym *s)
{
	int m1, m2, d, wmeasure, nm, in_parenth;
	unsigned i;
	char *q;
static char *top_err = "Cannot identify meter top";

	if (*p == '\0')
		return "Empty meter string";
	nm = 0;
	in_parenth = 0;
	wmeasure = 0;
	m1 = 0;
	if (strncmp(p, "none", 4) == 0) {
		p += 4;				/* no meter */
	} else while (*p != '\0') {
		if (*p == '=')
			break;
		if (nm >= MAX_MEASURE)
			return "Too many values in M:";
		switch (*p) {
		case 'C':
			s->u.meter.meter[nm].top[0] = *p++;
			if (*p == '|')
				s->u.meter.meter[nm].top[1] = *p++;
			m1 = 4;
			m2 = 4;
			break;
		case 'c':
		case 'o':
			if (*p == 'c')
				m1 = 4;
			else
				m1 = 3;
			m2 = 4;
			s->u.meter.meter[nm].top[0] = *p++;
			if (*p == '.')
				s->u.meter.meter[nm].top[1] = *p++;
			break;
		case '(':
			if (p[1] == '(') {	/* "M:5/4 ((2+3)/4)" */
				in_parenth = 1;
				s->u.meter.meter[nm++].top[0] = *p++;
			}
			q = p + 1;
			while (*q != '\0') {
				if (*q == ')' || *q == '/')
					break;
				q++;
			}
			if (*q == ')' && q[1] == '/') {	/* "M:5/4 (2+3)/4" */
				p++;		/* remove the parenthesis */
				continue;
			}			/* "M:5 (2+3)" */
			/* fall thru */
		case ')':
			in_parenth = *p == '(';
			s->u.meter.meter[nm++].top[0] = *p++;
			continue;
		default:
			if (sscanf(p, "%d", &m1) != 1
			 || m1 <= 0)
				return top_err;
			i = 0;
			m2 = 2;			/* default when no bottom value */
			for (;;) {
				while (isdigit((unsigned char) *p)
				    && i < sizeof s->u.meter.meter[0].top)
					s->u.meter.meter[nm].top[i++] = *p++;
				if (*p == ')') {
					if (p[1] != '/')
						break;
					p++;
				}
				if (*p == '/') {
					p++;
					if (sscanf(p, "%d", &m2) != 1
					 || m2 <= 0)
						return "Cannot identify meter bottom";
					i = 0;
					while (isdigit((unsigned char) *p)
					    && i < sizeof s->u.meter.meter[0].bot)
						s->u.meter.meter[nm].bot[i++] = *p++;
					break;
				}
				if (*p != ' ' && *p != '+')
					break;
				if (*p == '\0' || p[1] == '(')	/* "M:5 (2/4+3/4)" */
					break;
				if (i < sizeof s->u.meter.meter[0].top)
					s->u.meter.meter[nm].top[i++] = *p++;
				if (sscanf(p, "%d", &d) != 1
				 || d <= 0)
					return top_err;
				if (p[-1] == ' ') {
					if (d > m1)
						m1 = d;
				} else {
					m1 += d;
				}
			}
			break;
		}
		if (!in_parenth)
			wmeasure += m1 * BASE_LEN / m2;
		nm++;
		if (*p == ' ')
			p++;
		else if (*p == '+')
			s->u.meter.meter[nm++].top[0] = *p++;
	}
	meter = m1;
	if (*p == '=') {
		if (sscanf(++p, "%d/%d", &m1, &m2) != 2
		 || m1 <= 0
		 || m2 <= 0)
			return "Cannot identify meter explicit duration";
		wmeasure = m1 * BASE_LEN / m2;
		s->u.meter.expdur = 1;
	}
	s->u.meter.wmeasure = wmeasure;
	s->u.meter.nmeter = nm;

	/* if in the header, change the unit note length */
	if (parse.abc_state == ABC_S_HEAD && ulen == 0) {
		if (wmeasure >= BASE_LEN * 3 / 4
		 || wmeasure == 0)
			ulen = BASE_LEN / 8;
		else
			ulen = BASE_LEN / 16;
	}
	return 0;
}

/* -- get a possibly quoted string -- */
char *get_str(char *d,		/* destination */
	      char *s,		/* source */
	      int maxlen)	/* max length */
{
	char c;

	maxlen--;		/* have place for the EOS */
	while (isspace((unsigned char) *s))
		s++;
	if (*s == '"') {
		s++;
		while ((c = *s) != '\0') {
			if (c == '"') {
				s++;
				break;
			}
			if (c == '\\') {
				if (--maxlen > 0)
					*d++ = c;
				c = *++s;
			}
			if (--maxlen > 0)
				*d++ = c;
			s++;
		}
	} else {
		while ((c = *s) != '\0') {
			if (isspace((unsigned char) c))
				break;
			if (--maxlen > 0)
				*d++ = c;
			s++;
		}
	}
	*d = '\0';
	while (isspace((unsigned char) *s))
		s++;
	return s;
}

/* -- parse a tempo (Q:) -- */
static char *parse_tempo(char *p,
			 struct abcsym *s)
{
	int l;
	char *q, str[80];

	/* string before */
	if (*p == '"') {
		p = get_str(str, p, sizeof str);
		s->u.tempo.str1 = getarena(strlen(str) + 1);
		strcpy(s->u.tempo.str1, str);
	}

	/* beat */
	if (*p == 'C' || *p == 'c'
	 || *p == 'L' || *p == 'l') {
		int len;

		p++;
		if (*p != '=')
			goto inval;
		p = parse_len(p + 1, &len);
		if (len <= 0)
			goto inval;
		s->u.tempo.length[0] = len * ulen / BASE_LEN;
		while (isspace((unsigned char) *p))
			p++;
		if (parse.abc_vers >= (2 << 16))
			syntax("Deprecated Q: value", p);
	} else if (isdigit((unsigned char) *p) && strchr(p, '/') != 0) {
		unsigned i;

		i = 0;
		while (isdigit((unsigned char) *p)) {
			int top, bot, n;

			if (sscanf(p, "%d /%d%n", &top, &bot, &n) != 2
			 || bot <= 0)
				goto inval;
			l = (BASE_LEN * top) / bot;
			if (l <= 0
			 || i >= sizeof s->u.tempo.length
					/ sizeof s->u.tempo.length[0])
				goto inval;
			s->u.tempo.length[i++] = l;
			p += n;
			while (isspace((unsigned char) *p))
				p++;
		}
	}

	/* tempo value ('Q:beat=value' or 'Q:value') */
	if (*p == '=') {
		p++;
		while (isspace((unsigned char) *p))
			p++;
	}
	if (*p != '\0' && *p != '"') {
		q = p;
		while (*p != '"' && *p != '\0')
			p++;
		while (isspace((unsigned char) p[-1]))
			p--;
		l = p - q;
		s->u.tempo.value = getarena(l + 1);
		strncpy(s->u.tempo.value, q, l);
		s->u.tempo.value[l] = '\0';
		while (isspace((unsigned char) *p))
			p++;
	}

	/* string after */
	if (*p == '"') {
		p = get_str(str, p, sizeof str);
		s->u.tempo.str2 = getarena(strlen(str) + 1);
		strcpy(s->u.tempo.str2, str);
	}

	if (!s->u.tempo.str1 && !s->u.tempo.str2
	 && s->u.tempo.length[0] == 0) {
		if (s->u.tempo.value == 0)
			return "Empty tempo";
		if (parse.abc_vers >= (2 << 16))
			syntax("Deprecated Q: value", p);
	}
	return 0;
inval:
	return "Invalid tempo";
}

/* -- get a user defined symbol (U:) -- */
static char *get_user(char *p,
		      struct abcsym *s)
{
	unsigned char c;
	char *value;

	c = (unsigned char) *p++;
	if (c == '\\') {
		c = (unsigned char) *p++;
		switch (c) {
		case 'n':
			c = '\n';
			break;
		case 't':
			c = '\t';
			break;
		}
	}
	switch (char_tb[c]) {
	default:
		return "Bad decoration character";
	case CHAR_DECO:
		break;
	case CHAR_BAD:
	case CHAR_IGN:
	case CHAR_SPAC:
	case CHAR_DECOS:
	case CHAR_LINEBREAK:
		char_tb[c] = CHAR_DECO;
		break;
	}
	s->u.user.symbol = c;

	/* skip '=' */
	while (isspace((unsigned char) *p) || *p == '=')
		p++;
	if (char_tb[(unsigned char) *p] == CHAR_DECOS)
		p++;
/*fixme: 'U: <char> = "text"' is not treated */
	get_deco(p, &s->u.user.value);

	/* treat special pseudo decorations */
	value = parse.deco_tb[s->u.user.value - 128];
	if (strcmp(value, "beambreak") == 0)
		char_tb[c] = CHAR_SPAC;
	else if (strcmp(value, "ignore") == 0)
		char_tb[c] = CHAR_IGN;
	else if (strcmp(value, "nil") == 0
	      || strcmp(value, "none") == 0)
		char_tb[c] = CHAR_BAD;
	else
		return 0;
	s->u.user.value	= 0;		/* not a decoration */
	return 0;
}

/* -- parse the voice parameters (V:) -- */
static char *parse_voice(char *p,
			 struct abcsym *s)
{
	int voice;
	char *error_txt = NULL;
	char name[VOICE_NAME_SZ];
	char *clef_name, *clef_middle, *clef_lines, *clef_scale;
	char *p_octave, *p_cue;
	signed char *p_stem;
static struct kw_s {
	char *name;
	short len;
	short index;
} kw_tb[] = {
	{"name=", 5, 0},
	{"nm=", 3, 0},
	{"subname=", 8, 1},
	{"sname=", 6, 1},
	{"snm=", 4, 1},
	{"merge", 5, 2},
	{"up", 2, 3},
	{"down", 4, 4},
	{"stem=", 5, 5},
	{"gstem=", 6, 6},
	{"auto", 4, 7},
	{"dyn=", 4, 8},
	{"lyrics=", 7, 9},
	{"scale=", 6, 10},
	{"gchord=", 7, 11},
	{0}
};
	struct kw_s *kw;

	/* save the parameters of the previous voice */
	curvoice->ulen = ulen;
	curvoice->microscale = microscale;

	if (voice_tb[0].id[0] == '\0') {
		switch (s->prev->type) {
		case ABC_T_EOLN:
		case ABC_T_NOTE:
		case ABC_T_REST:
		case ABC_T_BAR:
			/* the previous voice was implicit (after K:) */
			voice_tb[0].id[0] = '1';
			break;
		}
	}
	{
		char *id, sep;

		id = p;
		while (isalnum((unsigned char) *p) || *p == '_')
			p++;
		sep = *p;
		*p = '\0';
		if (voice_tb[0].id[0] == '\0') {
			voice = 0;			/* first voice */
		} else {
			for (voice = 0; voice <= nvoice; voice++) {
				if (strcmp(id, voice_tb[voice].id) == 0)
					goto found;
			}
			if (voice >= MAXVOICE) {
				syntax("Too many voices", id);
				voice--;
			}
		}
		nvoice = voice;
		strncpy(voice_tb[voice].id, id, sizeof voice_tb[voice].id - 1);
		voice_tb[voice].mvoice = voice;
	found:
		strcpy(s->u.voice.id, voice_tb[voice].id);
		*p = sep;
	}
	curvoice = &voice_tb[voice];
	s->u.voice.voice = voice;

	/* if in tune, set the voice parameters */
	if (parse.abc_state == ABC_S_TUNE) {
		ulen = curvoice->ulen;
		microscale = curvoice->microscale;
	}

	/* parse the other parameters */
	clef_name = clef_middle = clef_lines = clef_scale = NULL;
	p_octave = p_cue = NULL;
	p_stem = &s->u.voice.stem;
	for (;;) {
		while (isspace((unsigned char) *p))
			p++;
		if (*p == '\0')
			break;
		p = parse_extra(p, &clef_name, &clef_middle, &clef_lines,
				&clef_scale, &p_octave, &p_cue);
		if (*p == '\0')
			break;
		for (kw = kw_tb; kw->name; kw++) {
			if (strncmp(p, kw->name, kw->len) == 0)
				break;
		}
		if (!kw->name) {
			while (!isspace((unsigned char) *p) && *p != '\0')
				p++;	/* ignore unknown keywords */
			continue;
		}
		p += kw->len;
		switch (kw->index) {
		case 0:			/* name */
			p = get_str(name, p, VOICE_NAME_SZ);
			s->u.voice.fname = getarena(strlen(name) + 1);
			strcpy(s->u.voice.fname, name);
			break;
		case 1:			/* subname */
			p = get_str(name, p, VOICE_NAME_SZ);
			s->u.voice.nname = getarena(strlen(name) + 1);
			strcpy(s->u.voice.nname, name);
			break;
		case 2:			/* merge */
			s->u.voice.merge = 1;
			break;
		case 3:			/* up */
			*p_stem = 1;
			break;
		case 4:			/* down */
			*p_stem = -1;
			break;
		case 5:			/* stem= */
			p_stem = &s->u.voice.stem;
			break;
		case 6:			/* gstem= */
			p_stem = &s->u.voice.gstem;
			break;
		case 7:			/* auto */
			*p_stem = 2;
			break;
		case 8:			/* dyn= */
			p_stem = &s->u.voice.dyn;
			break;
		case 9:			/* lyrics= */
			p_stem = &s->u.voice.lyrics;
			break;
		case 10: {		/* scale= */
			float sc;

			sc = atof(p);
			if (sc >= 0.5 && sc <= 2)
				s->u.voice.scale = sc;
			else
				error_txt = "Bad value for voice scale";
			while (!isspace((unsigned char) *p) && *p != '\0')
				p++;
			break;
		    }
		case 11:		/* gchord= */
			p_stem = &s->u.voice.gchord;
			break;
		}
	}

	s->u.voice.octave = parse_octave(p_octave);
	if (p_cue) {
		if (strncmp(p_cue, "on", 2) == 0)
			s->u.voice.cue = 1;
		else
			s->u.voice.cue = -1;
	}
	if (clef_lines) {
		int l;

		l = atoi(clef_lines);
		if ((unsigned) l < 10)
			s->u.voice.stafflines = l;
		else
			syntax("Bad value of stafflines", clef_lines);
	} else {
		s->u.voice.stafflines = -1;
	}
	if (clef_scale) {
		float sc;

		sc = atof(clef_scale);
		if (sc >= 0.5 && sc <= 3)
			s->u.voice.staffscale = sc;
		else
			syntax("Bad value of staffscale", clef_scale);
	}

	if (clef_name || clef_middle) {
		s = abc_new(ABC_T_CLEF, NULL);
		parse_clef(s, clef_name, clef_middle);
	}
	return error_txt;
}

/* -- parse a bar -- */
static char *parse_bar(char *p)
{
	struct abcsym *s;
	int bar_type;
	char repeat_value[32];

	p--;
	bar_type = 0;
	for (;;) {
		switch (*p++) {
		case '|':
			bar_type <<= 4;
			bar_type |= B_BAR;
			continue;
		case '[':
			bar_type <<= 4;
			bar_type |= B_OBRA;
			continue;
		case ']':
			bar_type <<= 4;
			bar_type |= B_CBRA;
			continue;
		case ':':
			bar_type <<= 4;
			bar_type |= B_COL;
			continue;
		default:
			break;
		}
		break;
	}
	p--;

	/* if the last element is '[', it may start
	 * a chord, an embedded header or an other bar */
	if ((bar_type & 0x0f) == B_OBRA && bar_type != B_OBRA
	 && *p != ' ') {
		bar_type >>= 4;
		p--;
	}

	if (bar_type == (B_OBRA << 8) + (B_BAR << 4) + B_CBRA)	/* [|] */
		bar_type = (B_OBRA << 4) + B_CBRA;		/* [] */

/*	curvoice->last_note = NULL; */
	if (vover > 0) {
		curvoice = &voice_tb[curvoice->mvoice];
		vover = 0;
	}
	s = abc_new(ABC_T_BAR, gchord);
	if (gchord)
		gchord = NULL;
	s->u.bar.type = bar_type;

	if (dc.n > 0) {
		memcpy(&s->u.bar.dc, &dc, sizeof s->u.bar.dc);
		dc.n = 0;
	}

	if (!lyric_started) {
		lyric_started = 1;
		s->flags |= ABC_F_LYRIC_START;
	}

	if (!isdigit((unsigned char) *p)	/* if not a repeat bar */
	 && (*p != '"' || p[-1] != '['))	/* ('["' only) */
		return p;

	if (*p == '"') {
		p = get_str(repeat_value, p, sizeof repeat_value);
	} else {
		char *q;

		q = repeat_value;
		while (isdigit((unsigned char) *p)
		    || *p == ','
		    || *p == '-'
		    || (*p == '.' && isdigit((unsigned char) p[1]))) {
			if (q < &repeat_value[sizeof repeat_value - 1])
				*q++ = *p++;
			else
				p++;
		}
		*q = '\0';
	}
	if (bar_type != B_OBRA
	 || s->text) {
		s = abc_new(ABC_T_BAR, repeat_value);
		s->u.bar.type = B_OBRA;
	} else {
		s->text = getarena(strlen(repeat_value) + 1);
		strcpy(s->text, repeat_value);
	}
	s->u.bar.repeat_bar = 1;
	return p;
}

/* -- parse note or rest with pitch and length -- */
/* in case of error, 'accidental' is set to -1 */
char *parse_basic_note(char *p,
			int *pitch,
			int *length,
			int *accidental,
			int *stemless)
{
	int pit, len, acc, nostem;

	acc = pit = nostem = 0;

	/* look for accidental sign */
	switch (*p) {
	case '^':
		p++;
		if (*p == '^') {
			p++;
			acc = A_DS;
		} else {
			acc = A_SH;
		}
		break;
	case '=':
		p++;
		acc = A_NT;
		break;
	case '_':
		p++;
		if (*p == '_') {
			p++;
			acc = A_DF;
		} else {
			acc = A_FT;
		}
		break;
	}

	/* look for microtone value */
	if (acc != 0
	 && (isdigit((unsigned char) *p)
	  || (*p == '/' && microscale == 0))) {
		int n, d;
		char *q;

		n = d = 1;
		if (*p != '/') {
			n = strtol(p, &q, 10);
			p = q;
		}
		if (*p == '/') {
			p++;
			if (!isdigit((unsigned char) *p)) {
				d = 2;
			} else {
				d = strtol(p, &q, 10);
				p = q;
			}
		}
		if (microscale == 0) {
			d--;
			d += (n - 1) << 8;	/* short [ (n-1) | (d-1) ] */
			for (n = 1; n < MAXMICRO; n++) {
				if (parse.micro_tb[n] == d)
					break;
				if (parse.micro_tb[n] == 0) {
					parse.micro_tb[n] = d;
					break;
				}
			}
			if (n == MAXMICRO) {
				syntax("Too many microtone accidentals", p);
				n = 0;
			}
		}
		acc += (n << 3);
	}

	/* get the pitch */
	{
		char *p_n;

		p_n = strchr(all_notes, *p);
		if (!p_n) {
			syntax(acc ? "Missing note after accidental"
				   : "Not a note", p);
			acc = -1;
			if (*p == '\0')
				p--;
		} else {
			pit = p_n - all_notes + 16;
		}
		p++;
	}
	while (*p == '\'') {		/* eat up following ' chars */
		pit += 7;
		p++;
	}
	while (*p == ',') {		/* eat up following , chars */
		pit -= 7;
		p++;
	}
	if (*p == '0') {
		nostem = 1;
		p++;
	}
	p = parse_len(p, &len);

	*pitch = pit;
	*length = len;
	*accidental = acc;
	*stemless = nostem;
	return p;
}

/* -- parse the decorations of notes and bars -- */
char *parse_deco(char *p,
		 struct decos *deco,
		 int m)			/* note index */
{
	int n;
	unsigned char t;

	n = deco->n;
	for (;;) {
		t = (unsigned char) *p++;
		if (char_tb[t] != CHAR_DECO && char_tb[t] != CHAR_DECOS)
			break;
		if (char_tb[t] == CHAR_DECOS)
			p = get_deco(p, &t);
		if (n >= MAXDC) {
			syntax("Too many decorations for the note", p);
		} else if (t != 0) {
			deco->tm[n].t = t;
			deco->tm[n++].m = m;
		}
	}
	deco->n = n;
	return p - 1;
}

/* -- parse a decoration line (d: or s:) -- */
static char *parse_decoline(char *p)
{
	struct abcsym *is;
	unsigned char t;
	int n;

	if ((is = deco_cont) == NULL)
		is = deco_start;
	else
		deco_cont = NULL;

	/* scan the decoration line */
	while (*p != '\0') {
		while (isspace((unsigned char) *p))
			p++;
		if (*p == '\0')
			break;
		switch (*p) {
		case '|':
			while (is && (is->type != ABC_T_BAR
					|| is->u.bar.type == B_OBRA))
				is = is->next;
			if (!is) {
				syntax("Not enough bar lines for deco line", p);
				return NULL;
			}
			is = is->next;
			p++;
			continue;
		case '*':
			while (is && is->type != ABC_T_NOTE)
				is = is->next;
			if (!is) {
				syntax("Not enough notes for deco line", p);
				return NULL;
			}
			is = is->next;
			p++;
			continue;
		case '\\':
			if (p[1] == '\0') {
				if (!is)
					return "Not enough notes for deco line";
				deco_cont = is;
				return NULL;
			}
			syntax("'\\' ignored", p);
			p++;
			continue;
		case '"':
			p = parse_gchord(p + 1);
			break;
		default:
			if (char_tb[(unsigned char) *p] == CHAR_DECOS)
				p = get_deco(p + 1, &t);
			else
				t = (unsigned char) *p++;
			break;
		}

		/* store the decoration and gchord/annotation in the next note */
		while (is && (is->type != ABC_T_NOTE
				|| (is->flags & ABC_F_GRACE)))
			is = is->next;
		if (!is)
			return "Not enough notes for deco line";

		if (gchord) {
			if (is->text) {
				char *gch;

				n = strlen(is->text);
				gch = getarena(n + strlen(gchord) + 2);
				strcpy(gch, is->text);
				gch[n] = '\n';
				strcpy(gch + n + 1, gchord);
				gchord = gch;
			}
			is->text = gchord;
			gchord = NULL;
		} else {
			n = is->u.note.dc.n;
			if (n >= MAXDC) {
				syntax("Too many decorations for the note", p);
			} else if (t != 0) {
				is->u.note.dc.tm[n].t = t;
				is->u.note.dc.tm[n++].m = 255;
				is->u.note.dc.n = n;
			}
		}
		is = is->next;
	}
	return NULL;
}

/* -- parse a guitar chord / annotation -- */
static char *parse_gchord(char *p)
{
	char *q;
	int l, l2;

	q = p;
	while (*p != '"') {
		if (*p == '\\')
			p++;
		if (*p == '\0') {
			syntax("No end of guitar chord", p);
			break;
		}
		p++;
	}
	l = p - q;
	if (gchord) {
		char *gch;

		/* many guitar chords: concatenate with '\n' */
		l2 = strlen(gchord);
		gch = getarena(l2 + 1 + l + 1);
		strcpy(gch, gchord);
		gch[l2++] = '\n';
		strncpy(&gch[l2], q, l);
		gch[l2 + l] = '\0';
		gchord = gch;
	} else {
		gchord = getarena(l + 1);
		strncpy(gchord, q, l);
		gchord[l] = '\0';
	}
	if (*p != '\0')
		p++;
	return p;
}

/* -- parse a note length -- */
static char *parse_len(char *p,
			int *p_len)
{
	int len, fac;
	char *q;

	len = BASE_LEN;
	if (isdigit((unsigned char) *p)) {
		len *= strtol(p, &q, 10);
		if (len <= 0) {
			syntax("Bad length", p);
			len = BASE_LEN;
		}
		p = q;
	}
	fac = 1;
	while (*p == '/') {
		p++;
		if (isdigit((unsigned char) *p)) {
			fac *= strtol(p, &q, 10);
			if (fac == 0) {
				syntax("Bad length divisor", p - 1);
				fac = 1;
			}
			p = q;
		} else {
			fac *= 2;
		}
	}
	if (len % fac)
		syntax("Bad length divisor", p - 1);
	len /= fac;
	*p_len = len;
	return p;
}

/* -- parse a ABC line -- */
/* return 1 on end of tune, and 2 on start of new tune */
static int parse_line(char *p)
{
	struct abcsym *s;
	char *q, c;
	struct abcsym *last_note_sav = NULL;
	struct decos dc_sav;
	int i, flags, flags_sav = 0, slur;
	static char qtb[10] = {0, 1, 3, 2, 3, 0, 2, 0, 3, 0};

	colnum = 0;
	switch (*p) {
	case '\0':			/* blank line */
		switch (parse.abc_state) {
		case ABC_S_GLOBAL:
			if (parse.last_sym
			 && parse.last_sym->type != ABC_T_NULL)
				s = abc_new(ABC_T_NULL, NULL);
		case ABC_S_HEAD:	/*fixme: may have blank lines in headers?*/
			return 0;
		}
		return 1;
	case '%':
		if (p[1] == '%') {
			s = abc_new(ABC_T_PSCOM, p);
			p += 2;				/* skip '%%' */
			if (strncasecmp(p, "decoration ", 11) == 0) {
				p += 11;
				while (isspace((unsigned char) *p))
					p++;
				switch (*p) {
				case '!':
					char_tb['!'] = CHAR_DECOS;
					char_tb['+'] = CHAR_BAD;
					break;
				case '+':
					char_tb['+'] = CHAR_DECOS;
					char_tb['!'] = CHAR_BAD;
					break;
				}
				return 0;
			}
			if (strncasecmp(p, "linebreak ", 10) == 0) {
				for (i = 0; i < sizeof char_tb; i++) {
					if (char_tb[i] == CHAR_LINEBREAK)
						char_tb[i] = i != '!' ?
								CHAR_BAD :
								CHAR_DECOS;
				}
				p += 10;
				for (;;) {
					while (isspace((unsigned char) *p))
						p++;
					if (*p == '\0')
						break;
					switch (*p) {
					case '!':
					case '$':
					case '*':
					case ';':
					case '?':
					case '@':
						char_tb[(unsigned char) *p++]
								= CHAR_LINEBREAK;
						break;
					case '<':
						if (strncmp(p, "<none>", 6) == 0)
							return 0;
						if (strncmp(p, "<EOL>", 5) == 0) {
							char_tb['\n'] = CHAR_LINEBREAK;
							p += 5;
							break;
						}
						/* fall thru */
					default:
						if (strcmp(p, "lock") != 0)
							syntax("Invalid character in %%%%linebreak",
								p);
						return 0;
					}
				}
				return 0;
			}
			if (strncasecmp(p, "microscale ", 11) == 0) {
				int v;

				p += 11;
				while (isspace((unsigned char) *p))
					p++;
				sscanf(p, "%d", &v);
				if (v < 4 || v >= 256)
					syntax("Invalid value in %%microscale", p);
				else
					microscale = v;
				return 0;
			}
			if (strncasecmp(p, "user ", 5) == 0) {
				p += 5;
				while (isspace((unsigned char) *p))
					p++;
				get_user(p, s);
				return 0;
			}
			return 0;
		}
		/* fall thru */
	case '\\':				/* abc2mtex specific lines */
		return 0;			/* skip */
	}

	/* header fields */
	if (p[1] == ':'
	 && *p != '|' && *p != ':') {		/* not '|:' nor '::' */
		int new_tune;

		new_tune = parse_info(p);

		/* handle BarFly voice definition */
		/* 'V:n <note line ending with a bar>' */
		if (*p != 'V'
		 || parse.abc_state != ABC_S_TUNE)
			return new_tune;		/* (normal return) */
		c = p[strlen(p) - 1];
		if (c != '|' && c != ']')
			return new_tune;
		while (!isspace((unsigned char) *p) && *p != '\0')
			p++;
		while (isspace((unsigned char) *p))
			p++;
	}
	if (parse.abc_state != ABC_S_TUNE)
		return 0;

	/* music */
	flags = 0;
	if (parse.abc_vers <= (2 << 16))
		lyric_started = 0;
	deco_start = deco_cont = NULL;
	slur = 0;
	while (*p != '\0') {
		colnum = p - abc_line;
		switch (char_tb[(unsigned char) *p++]) {
		case CHAR_GCHORD:			/* " */
			if (flags & ABC_F_GRACE)
				goto bad_char;
			p = parse_gchord(p);
			break;
		case CHAR_GR_ST:		/* '{' */
			if (flags & ABC_F_GRACE)
				goto bad_char;
			last_note_sav = curvoice->last_note;
			curvoice->last_note = NULL;
			memcpy(&dc_sav, &dc, sizeof dc);
			dc.n = 0;
			flags_sav = flags;
			flags = ABC_F_GRACE;
			if (*p == '/') {
				flags |= ABC_F_SAPPO;
				p++;
			}
			break;
		case CHAR_GR_EN:		/* '}' */
			if (!(flags & ABC_F_GRACE))
				goto bad_char;
			parse.last_sym->flags |= ABC_F_GR_END;
			curvoice->last_note = last_note_sav;
			memcpy(&dc, &dc_sav, sizeof dc);
			flags = flags_sav;
			break;
		case CHAR_DECOS:
			if (p[-1] == '!'
			 && char_tb['\n'] == CHAR_LINEBREAK
			 && check_nl(p)) {
				s = abc_new(ABC_T_EOLN, NULL);	/* abc2win EOL */
				s->u.eoln.type = 2;
				break;
			}
			/* fall thru */
		case CHAR_DECO:
			if (p[-1] == '.') {
				if (*p == '(' || *p == '-')
					break;
				if (*p == '|') {
					p = parse_bar(p + 1);
					parse.last_sym->u.bar.dotted = 1;
					break;
				}
			}
			p = parse_deco(p - 1, &dc, 255);
			break;
		case CHAR_LINEBREAK:
			s = abc_new(ABC_T_EOLN, NULL);
//			s->u.eoln.type = 0;
			break;
		case CHAR_NOTE:
			p = parse_note(p - 1, flags);
			flags &= ABC_F_GRACE;
			parse.last_sym->u.note.slur_st = slur;
			slur = 0;
			if (parse.last_sym->u.note.lens[0] > 0)	/* if not space */
				curvoice->last_note = parse.last_sym;
			break;
		case CHAR_SLASH:		/* '/' */
			if (flags & ABC_F_GRACE)
				goto bad_char;
			q = p;
			while (*q == '/')
				q++;
			if (char_tb[(unsigned char) *q] != CHAR_BAR)
				goto bad_char;
			s = abc_new(ABC_T_MREP, NULL);
			s->u.bar.type = 0;
			s->u.bar.len = q - p + 1;
			syntax("Non standard measure repeat syntax", p - 1);
			p = q;
			break;
		case CHAR_BSLASH:		/* '\\' */
			if (*p == '\0')
				break;
			syntax("'\\' ignored", p - 1);
			break;
		case CHAR_OBRA:			/* '[' */
			if (*p == '|' || *p == ']' || *p == ':'
			 || isdigit((unsigned char) *p) || *p == '"'
			 || *p == ' ') {
				if (flags & ABC_F_GRACE)
					goto bad_char;
				p = parse_bar(p);
				break;
			}
			if (p[1] != ':') {
				p = parse_note(p - 1, flags); /* chord */
				flags &= ABC_F_GRACE;
				parse.last_sym->u.note.slur_st = slur;
				slur = 0;
				curvoice->last_note = parse.last_sym;
				break;
			}

			/* embedded information field */
#if 0
/*fixme:OK for [I:staff n], ?? for other headers*/
			if (flags & ABC_F_GRACE)
				goto bad_char;
#endif
			while (p[2] == ' ') {		/* remove the spaces */
				p[2] = ':';
				p[1] = *p;
				p++;
			}
			c = ']';
			q = p;
			while (*p != '\0' && *p != c)
				p++;
			if (*p == '\0') {
				syntax("Escape sequence [..] not closed", q);
				c = '\0';
			} else {
				*p = '\0';
			}
			parse_info(q);
			*p = c;
			if (c != '\0')
				p++;
			break;
		case CHAR_BAR:			/* '|', ':' or ']' */
			if (flags & ABC_F_GRACE)
				goto bad_char;
			p = parse_bar(p);
			break;
		case CHAR_OPAR:			/* '(' */
			if (*p > '0' && *p <= '9') {
				int pplet, qplet, rplet;

				pplet = strtol(p, &q, 10);
				p = q;
				if ((unsigned) pplet < sizeof qtb / sizeof qtb[0])
					qplet = qtb[pplet];
				else
					qplet = qtb[0];
				rplet = pplet;
				if (*p == ':') {
					p++;
					if (isdigit((unsigned char) *p)) {
						qplet = strtol(p, &q, 10);
						p = q;
					}
					if (*p == ':') {
						p++;
						if (isdigit((unsigned char) *p)) {
							rplet = strtol(p, &q, 10);
							p = q;
						}
					}
				}
				if (rplet < 1) {
					syntax("Invalid 'r' in tuplet", p);
					break;
				}
				if (qplet == 0)
					qplet = meter % 3 == 0 ? 3 : 2;
				s = abc_new(ABC_T_TUPLET, NULL);
				s->u.tuplet.p_plet = pplet;
				s->u.tuplet.q_plet = qplet;
				s->u.tuplet.r_plet = rplet;
				s->flags |= flags;
				break;
			}
			if (*p == '&') {
				if (flags & ABC_F_GRACE)
					goto bad_char;
				p++;
				if (vover != 0) {
					syntax("Nested voice overlay", p - 1);
					break;
				}
				s = abc_new(ABC_T_V_OVER, NULL);
				s->u.v_over.type = V_OVER_S;
				s->u.v_over.voice = curvoice - voice_tb;
				vover = -1;		/* multi-bars */
				break;
			}
			slur <<= 3;
			if (p[-2] == '.' && dc.n == 0)
				slur |= SL_DOTTED;
			switch (*p) {
			case '\'':
				slur += SL_ABOVE;
				p++;
				break;
			case ',':
				slur += SL_BELOW;
				p++;
				break;
			default:
				slur += SL_AUTO;
				break;
			}
			break;
		case CHAR_CPAR:			/* ')' */
			switch (parse.last_sym->type) {
			case ABC_T_NOTE:
			case ABC_T_REST:
				break;
			default:
				goto bad_char;
			}
			parse.last_sym->u.note.slur_end++;
			break;
		case CHAR_VOV:			/* '&' */
			if (flags & ABC_F_GRACE)
				goto bad_char;
			if (*p != ')'
			 || vover == 0) {		/*??*/
				if (!curvoice->last_note) {
					syntax("Bad start of voice overlay", p);
					break;
				}
				s = abc_new(ABC_T_V_OVER, NULL);
				/*s->u.v_over.type = V_OVER_V; */
				vover_new();
				s->u.v_over.voice = curvoice - voice_tb;
				if (vover == 0)
					vover = 1;	/* single bar */
				break;
			}
			p++;
			vover = 0;
			s = abc_new(ABC_T_V_OVER, NULL);
			s->u.v_over.type = V_OVER_E;
			s->u.v_over.voice = curvoice->mvoice;
			curvoice->last_note = NULL;	/* ?? */
			curvoice = &voice_tb[curvoice->mvoice];
			break;
		case CHAR_SPAC:			/* ' ' and '\t' */
			flags |= ABC_F_SPACE;
			break;
		case CHAR_MINUS: {		/* '-' */
			int tie_pos;

			if (!curvoice->last_note
			 || curvoice->last_note->type != ABC_T_NOTE)
				goto bad_char;
			if (p[-2] == '.' && dc.n == 0)
				tie_pos = SL_DOTTED;
			else
				tie_pos = 0;
			switch (*p) {
			case '\'':
				tie_pos += SL_ABOVE;
				p++;
				break;
			case ',':
				tie_pos += SL_BELOW;
				p++;
				break;
			default:
				tie_pos += SL_AUTO;
				break;
			}
			for (i = 0; i <= curvoice->last_note->u.note.nhd; i++) {
				if (curvoice->last_note->u.note.ti1[i] == 0)
					curvoice->last_note->u.note.ti1[i] = tie_pos;
				else if (curvoice->last_note->u.note.nhd == 0)
					syntax("Too many ties", p);
			}
			break;
		    }
		case CHAR_BRHY:			/* '>' and '<' */
			if (!curvoice->last_note)
				goto bad_char;
			i = 1;
			while (*p == p[-1]) {
				i++;
				p++;
			}
			if (i > 3) {
				syntax("Bad broken rhythm", p - 1);
				i = 3;
			}
			if (p[-1] == '<')
				i = -i;
			broken_rhythm(&curvoice->last_note->u.note, i);
			curvoice->last_note->u.note.brhythm = i;
			break;
		case CHAR_IGN:			/* '*' & '`' */
			break;
		default:
		bad_char:
			syntax((flags & ABC_F_GRACE)
					? "Bad character in grace note sequence"
					: "Bad character",
				p - 1);
			break;
		}
	}

/*fixme: may we have grace notes across lines?*/
	if (flags & ABC_F_GRACE) {
		syntax("EOLN in grace note sequence", p - 1);
		if (curvoice->last_note)
			curvoice->last_note->flags |= ABC_F_GR_END;
		curvoice->last_note = last_note_sav;
		memcpy(&dc, &dc_sav, sizeof dc);
	}

	/* add eoln */
	s = abc_new(ABC_T_EOLN, NULL);
	if (flags & ABC_F_SPACE)
		s->flags |= ABC_F_SPACE;
	if (p[-1] == '\\'
	 || char_tb['\n'] != CHAR_LINEBREAK)
		s->u.eoln.type = 1;		/* no break */
	return 0;
}

/* -- parse a note or a rest -- */
static char *parse_note(char *p,
			int flags)
{
	struct abcsym *s;
	char *q;
	int pit, len, acc, nostem, chord, j, m, n;

	if (flags & ABC_F_GRACE) {	/* in a grace note sequence */
		s = abc_new(ABC_T_NOTE, NULL);
	} else {
		s = abc_new(ABC_T_NOTE, gchord);
		if (gchord)
			gchord = NULL;
	}
	s->flags |= flags;

	if (!lyric_started) {
		lyric_started = 1;
		s->flags |= ABC_F_LYRIC_START;
	}
	if (*p != 'X' && *p != 'Z'
	 && !(flags & ABC_F_GRACE)) {
		if (!deco_start)
			deco_start = s;
	}
	chord = 0;

	/* rest */
	switch (*p) {
	case 'X':
		s->flags |= ABC_F_INVIS;
	case 'Z':			/* multi-rest */
		s->type = ABC_T_MREST;
		p++;
		len = 1;
		if (isdigit((unsigned char) *p)) {
			len = strtol(p, &q, 10);
			if (len == 0 && len > 100) {
				syntax("Bad number of measures", p);
				len = 1;
			}
			p = q;
		}
		s->u.bar.type = 0;
		s->u.bar.len = len;
		goto add_deco;
	case 'y':			/* space (BarFly) */
		s->type = ABC_T_REST;
		s->flags |= ABC_F_INVIS;
		p++;
		if (isdigit((unsigned char) *p)) {	/* number of points */
			s->u.note.lens[1] = strtol(p, &q, 10);
			p = q;
		} else {
			s->u.note.lens[1] = -1;
		}
		goto add_deco;
	case 'x':			/* invisible rest */
		s->flags |= ABC_F_INVIS;
		/* fall thru */
	case 'z':
		s->type = ABC_T_REST;
		p = parse_len(p + 1, &len);
		s->u.note.lens[0] = len * ulen / BASE_LEN;
		goto do_brhythm;
	case '[':			/* '[..]' = chord */
		chord = 1;
		p++;
		break;
	}

	q = p;

	/* get pitch, length and possible accidental */
	m = 0;
	nostem = 0;
	for (;;) {
		if (chord) {
			if (m >= MAXHD) {
				syntax("Too many notes in chord", p);
				m--;
			}
			n = 0;
			if (*p == '.') {
				n = SL_DOTTED;
				p++;
			}
			if (*p == '(') {
				p++;
				switch (*p) {
				case '\'':
					n += SL_ABOVE;
					p++;
					break;
				case ',':
					n += SL_BELOW;
					p++;
					break;
				default:
					n += SL_AUTO;
					break;
				}
				s->u.note.sl1[m] = (s->u.note.sl1[m] << 3)
							+ n;
			}
		}
		p = parse_deco(p, &dc, m);	/* note head decorations */
		p = parse_basic_note(p, &pit, &len, &acc, &n);
		if (!(flags & ABC_F_GRACE))
			len = len * ulen / BASE_LEN;
		else
			len /= 8;		/* for grace note alone */

		s->u.note.pits[m] = pit;
		s->u.note.lens[m] = len;
		s->u.note.accs[m] = acc;
		nostem |= n;

		if (chord) {
			for (;;) {
				if (*p == '.') {
					if (p[1] != '-')
						break;
					p++;
				}
				if (*p == '-') {
					switch (p[1]) {
					case '\'':
						s->u.note.ti1[m] = SL_ABOVE;
						p++;
						break;
					case ',':
						s->u.note.ti1[m] = SL_BELOW;
						p++;
						break;
					default:
						s->u.note.ti1[m] = SL_AUTO;
						break;
					}
				} else if (*p == ')') {
					s->u.note.sl2[m]++;
				} else {
					break;
				}
				p++;
			}
		}
		if (acc >= 0)			/* if no error */
			m++;			/* normal case */

		if (!chord)
			break;
		if (*p == ']') {
			p++;
			if (*p == '0') {
				nostem |= 1;
				p++;
			}
			if (*p == '/' || isdigit((unsigned char) *p)) {
				p = parse_len(p, &len);
				s->u.note.chlen = len;
				for (j = 0; j < m; j++) {
					n = len * s->u.note.lens[j];
					s->u.note.lens[j] = n / BASE_LEN;
				}
			}
			break;
		}
		if (*p == '\0') {
			syntax("Chord not closed", q);
			break;
		}
	}
	if (nostem)
		s->flags |= ABC_F_STEMLESS;

	if (m == 0)			/* if no note (or error) */
		goto err;

	s->u.note.microscale = microscale;
	s->u.note.nhd = m - 1;

	/* duplicate the head decorations on each note */
	for (n = dc.n; --n >= 0; ) {
		int t;

		if (dc.tm[n].m != 255)
			continue;
		t = dc.tm[n].t - 128;
		if (t <= 0)
			continue;
		if (strncmp(parse.deco_tb[t], "head-", 5) != 0)
			continue;
		dc.tm[n].m = 0;
		for (m = 1; m <= s->u.note.nhd; m++) {
			if (dc.n >= MAXDC) {
				syntax("too many head decorations", p);
				continue;
			}
			j = dc.n++;
			dc.tm[j].t = dc.tm[n].t;
			dc.tm[j].m = m;
		}
	}
do_brhythm:
	if (curvoice->last_note
	 && curvoice->last_note->u.note.brhythm != 0)
		broken_rhythm(&s->u.note,
			      -curvoice->last_note->u.note.brhythm);
add_deco:
	if (dc.n > 0) {
		memcpy(s->type != ABC_T_MREST ? &s->u.note.dc
				: &s->u.bar.dc,
			&dc, sizeof dc);
		dc.n = 0;
	}

	/* forbid rests in grace note sequences */
	if (s->type != ABC_T_NOTE && (flags & ABC_F_GRACE)) {
		syntax("Not a note in grace note sequence", p);
		goto err;
	}
	return p;

err:
	if ((parse.last_sym = s->prev) == NULL) {
		parse.first_sym = NULL;
	} else {
		s->prev->next = NULL;
		s->prev->flags |= (s->flags & ABC_F_ERROR);
	}
	return p;
}

/* -- parse an information field -- */
/* return 2 on start of new tune */
static int parse_info(char *p)
{
	struct abcsym *s;
	char info_type = *p;
	char *error_txt = NULL;

	s = abc_new(ABC_T_INFO, p);

	p += 2;

	switch (info_type) {
	case 'd':
	case 's':
		if (parse.abc_state == ABC_S_GLOBAL)
			break;
		if (!deco_start) {
			error_txt = "Erroneous 'd:'/'s:'";
			break;
		}
		error_txt = parse_decoline(p);
		break;
	case 'K':
		if (parse.abc_state == ABC_S_GLOBAL)
			break;
		parse_key(p, s);
		if (parse.abc_state == ABC_S_HEAD) {
			int i;

			parse.abc_state = ABC_S_TUNE;
			if (ulen == 0)
				ulen = BASE_LEN / 8;
			for (i = MAXVOICE; --i >= 0; )
				voice_tb[i].ulen = ulen;
			lyric_started = 0;
		}
		break;
	case 'L':
		error_txt = get_len(p, s);
		if (s->u.length.base_length > 0)
			ulen = s->u.length.base_length;
		break;
	case 'M':
		error_txt = parse_meter(p, s);
		break;
	case 'Q':
		error_txt = parse_tempo(p, s);
		break;
	case 'U':
		error_txt = get_user(p, s);
		break;
	case 'V':
		if (parse.abc_state == ABC_S_GLOBAL)
			break;
		error_txt = parse_voice(p, s);
		break;
	case 'X':
		memset(voice_tb, 0, sizeof voice_tb);
		nvoice = 0;
		curvoice = voice_tb;
		parse.abc_state = ABC_S_HEAD;
		lvlarena(1);
		return 2;
	}
	if (error_txt)
		syntax(error_txt, p);
	return 0;
}

/* -- print a syntax error message -- */
static void syntax(char *msg,
		   char *q)
{
	int n, len, m1, m2, pp;
	int maxcol = 73;

	severity = 1;
	n = q - abc_line;
	len = strlen(abc_line);
	if ((unsigned) n > (unsigned) len)
		n = -1;
	print_error(msg, n);
	if (n < 0) {
		if (q && *q != '\0')
			fprintf(stderr, " (near '%s')\n", q);
		return;
	}
	m1 = 0;
	m2 = len;
	if (m2 > maxcol) {
		if (n < maxcol) {
			m2 = maxcol;
		} else {
			m1 = n - 20;
			m2 = m1 + maxcol;
			if (m2 > len)
				m2 = len;
		}
	}

	fprintf(stderr, "%4d ", linenum);
	pp = 6;
	if (m1 > 0) {
		fprintf(stderr, "...");
		pp += 3;
	}
	fprintf(stderr, "%.*s", m2 - m1, &abc_line[m1]);
	if (m2 < len)
		fprintf(stderr, "...");
	fprintf(stderr, "\n");

	if ((unsigned) n < 200)
		fprintf(stderr, "%*s\n", n + pp - m1, "^");

	if (last_sym)
		last_sym->flags |= ABC_F_ERROR;
}

/* -- switch to a new voice overlay -- */
static void vover_new(void)
{
	int voice, mvoice;

	mvoice = curvoice->mvoice;
	for (voice = curvoice - voice_tb + 1; voice <= nvoice; voice++)
		if (voice_tb[voice].mvoice == mvoice)
			break;
	if (voice > nvoice) {
		if (nvoice >= MAXVOICE) {
			syntax("Too many voices", 0);
			return;
		}
		nvoice = voice;
		voice_tb[voice].id[0] = '&';
		voice_tb[voice].mvoice = mvoice;
	}
	voice_tb[voice].ulen = curvoice->ulen;
	voice_tb[voice].microscale = curvoice->microscale;
	curvoice = &voice_tb[voice];
}
abcm2ps-8.5.2/abcparse.h0000644000175000017500000001630612442626777013127 0ustar  jefjef/*++
 * Declarations for abcparse.c.
 *
 *-*/

#define MAXVOICE 32	/* max number of voices */

#define MAXHD	8	/* max heads in a chord */
#define MAXDC	32	/* max decorations per symbol */
#define MAXMICRO 32	/* max microtone values (5 bits in accs[]) */
#define DC_NAME_SZ 128	/* size of the decoration name table */

#define BASE_LEN 1536	/* basic note length (semibreve or whole note - same as MIDI) */

#define VOICE_ID_SZ 16	/* max size of the voice identifiers */

/* accidentals */
enum accidentals {
	A_NULL,		/* none */
	A_SH,		/* sharp */
	A_NT,		/* natural */
	A_FT,		/* flat */
	A_DS,		/* double sharp */
	A_DF		/* double flat */
};

/* bar types - 4 bits per symbol */
#define B_BAR 1		/* | */
#define B_OBRA 2	/* [ */
#define B_CBRA 3	/* ] */
#define B_COL 4		/* : */

/* slur/tie types (3 bits) */
#define SL_ABOVE 0x01
#define SL_BELOW 0x02
#define SL_AUTO 0x03
#define SL_DOTTED 0x04		/* (modifier bit) */

struct decos {		/* decorations */
	char n;			/* whole number of decorations */
	struct {
		unsigned char t;	/* decoration index */
		unsigned char m;	/* index in chord when note */
	} tm[MAXDC];
};

struct note {		/* note or rest */
	signed char pits[MAXHD]; /* pitches */
	short lens[MAXHD];	/* note lengths (# pts in [1] if space) */
	unsigned char accs[MAXHD]; /* code for accidentals & index in micro_tb */
	unsigned char sl1[MAXHD]; /* slur start per head */
	char sl2[MAXHD];	/* number of slur ends per head */
	char ti1[MAXHD];	/* flag to start tie here */
	short chlen;		/* chord length */
	char nhd;		/* number of notes in chord - 1 */
	unsigned char slur_st;	/* slurs starting here (2 bits array) */
	char slur_end;		/* number of slurs ending here */
	signed char brhythm;	/* broken rhythm */
	unsigned char microscale; /* microtone denominator - 1 */
	struct decos dc;	/* decorations */
};

/* symbol definition */
struct abctune;
struct abcsym {
	struct abctune *tune;	/* tune */
	struct abcsym *next, *prev; /* next / previous symbol */
	char type;		/* symbol type */
#define ABC_T_NULL	0
#define ABC_T_INFO 	1		/* (text[0] gives the info type) */
#define ABC_T_PSCOM	2
#define ABC_T_CLEF	3
#define ABC_T_NOTE	4
#define ABC_T_REST	5
#define ABC_T_BAR	6
#define ABC_T_EOLN	7
#define ABC_T_MREST	8		/* multi-measure rest */
#define ABC_T_MREP	9		/* measure repeat */
#define ABC_T_V_OVER	10		/* voice overlay */
#define ABC_T_TUPLET	11
	char state;		/* symbol state in file/tune */
#define ABC_S_GLOBAL 0			/* global */
#define ABC_S_HEAD 1			/* in header (after X:) */
#define ABC_S_TUNE 2			/* in tune (after K:) */
	unsigned short flags;
#define ABC_F_ERROR	0x0001		/* error around this symbol */
#define ABC_F_INVIS	0x0002		/* invisible symbol */
#define ABC_F_SPACE	0x0004		/* space before a note */
#define ABC_F_STEMLESS	0x0008		/* note with no stem */
#define ABC_F_LYRIC_START 0x0010	/* may start a lyric here */
#define ABC_F_GRACE	0x0020		/* grace note */
#define ABC_F_GR_END	0x0040		/* end of grace note sequence */
#define ABC_F_SAPPO	0x0080		/* short appoggiatura */
	unsigned short colnum;	/* ABC source column number */
	int linenum;		/* ABC source line number */
	char *fn;		/* ABC source file name */
	char *text;		/* main text (INFO, PSCOM),
				 * guitar chord (NOTE, REST, BAR) */
	char *comment;		/* comment part (when keep_comment) */
	union {			/* type dependent part */
		struct key_s {		/* K: info */
			signed char sf;		/* sharp (> 0) flats (< 0) */
			char empty;		/* clef alone if 1, 'none' if 2 */
			char exp;		/* exp (1) or mod (0) */
			char mode;		/* mode */
/* 0: Ionian, 1: Dorian, 2: Phrygian, 3: Lydian, 4: Mixolydian
 * 5: Aeolian, 6: Locrian, 7: major, 8:minor, 9: HP, 10: Hp */
#define MAJOR 7
#define MINOR 8
#define BAGPIPE 9				/* bagpipe when >= 8 */
			signed char nacc;	/* number of explicit accidentals */
						/* (-1) if no accidental */
			signed char cue;	/* cue voice (scale 0.7) */
			signed char octave;	/* 'octave=' */
#define NO_OCTAVE 10				/* no 'octave=' */
			unsigned char microscale; /* microtone denominator - 1 */
			signed char stafflines;
			float staffscale;
			signed char pits[8];
			unsigned char accs[8];
		} key;
		struct {		/* L: info */
			int base_length;	/* basic note length */
		} length;
		struct meter_s {	/* M: info */
			short wmeasure;		/* duration of a measure */
			unsigned char nmeter;	/* number of meter elements */
			char expdur;		/* explicit measure duration */
#define MAX_MEASURE 6
			struct {
				char top[8];	/* top value */
				char bot[2];	/* bottom value */
			} meter[MAX_MEASURE];
		} meter;
		struct {		/* Q: info */
			char *str1;		/* string before */
			short length[4];	/* up to 4 note lengths */
			char *value;		/* tempo value */
			char *str2;		/* string after */
		} tempo;
		struct {		/* V: info */
			char id[VOICE_ID_SZ];	/* voice ID */
			char *fname;		/* full name */
			char *nname;		/* nick name */
			float scale;		/* != 0 when change */
			unsigned char voice;	/* voice number */
			signed char octave;	/* 'octave=' - same as in K: */
			char merge;		/* merge with previous voice */
			signed char stem;	/* have stems up or down (2 = auto) */
			signed char gstem;	/* have grace stems up or down (2 = auto) */
			signed char dyn;	/* have dynamic marks above or below the staff */
			signed char lyrics;	/* have lyrics above or below the staff */
			signed char gchord;	/* have gchord above or below the staff */
			signed char cue;	/* cue voice (scale 0.7) */
			signed char stafflines;
			float staffscale;
		} voice;
		struct {		/* bar, mrest or mrep */
			int type;
			char repeat_bar;
			char len;		/* len if mrest or mrep */
			char dotted;
			struct decos dc;	/* decorations */
		} bar;
		struct clef_s {		/* clef */
			char *name;		/* PS drawing function */
			signed char type;
#define TREBLE 0
#define ALTO 1
#define BASS 2
#define PERC 3
#define AUTOCLEF 4
			char line;
			signed char octave;	/* '+8' / '-8' */
			signed char transpose;	/* if '^8' / '_8' */
			char invis;		/* clef 'none' */
			char check_pitch;	/* check if old abc2ps transposition */
		} clef;
		struct note note;	/* note, rest */
		struct {		/* user defined accent */
			unsigned char symbol;
			unsigned char value;
		} user;
		struct {
			char type;	/* 0: end of line
					 * 1: continuation ('\')
					 * 2: line break ('!') */
		} eoln;
		struct {		/* voice overlay */
			char type;
#define V_OVER_V 0				/* & */
#define V_OVER_S 1				/* (& */
#define V_OVER_E 2				/* &) */
			unsigned char voice;
		} v_over;
		struct {		/* tuplet */
			char p_plet, q_plet, r_plet;
		} tuplet;
	} u;
};

/* parse definition */
struct {
	struct abcsym *first_sym; /* first symbol */
	struct abcsym *last_sym; /* last symbol */
	int abc_vers;		/* ABC version = (H << 16) + (M << 8) + L */
	char *deco_tb[DC_NAME_SZ]; /* decoration names */
	unsigned short micro_tb[MAXMICRO]; /* microtone values [ (n-1) | (d-1) ] */
	int abc_state;		/* parser state */
} parse;

#ifdef WIN32
#define strcasecmp stricmp
#define strncasecmp _strnicmp
#define strdup _strdup
#ifdef _MSC_VER
#define fileno _fileno
#endif
#endif

//extern char *deco_tb[];
extern int severity;

void abc_parse(char *p, char *fname, int linenum);
void abc_eof(void);
char *get_str(char *d,
	      char *s,
	      int maxlen);
char *parse_basic_note(char *p,
			int *pitch,
			int *length,
			int *accidental,
			int *stemless);
char *parse_deco(char *p, struct decos *deco, int m);
abcm2ps-8.5.2/accordion.abc0000644000175000017500000000445111654555736013604 0ustar  jefjef% tablature examples
% --- diatonic accordion
%%beginps
% length x y n accordh - accordion tablature header with 2 or 3 lines
/accordh{
%	/Times-Roman 14 selectfont
	/nlines exch def
	.8 SLW
	gsave 20 add T /w exch def
	0 0 M w 0 RL
	0 3 M w 0 RL
	0 21 M w 0 RL
	0 39 M w 0 RL
	nlines 2 eq{
		0 42 M w 0 RL
		stroke
		/barh 42 def
		3 7 M (T:) show
		3 25 M (P:) show
	}{
		0 57 M w 0 RL
		0 60 M w 0 RL
		stroke
		/barh 60 def
		3 7 M (B/a:) show
		3 25 M (T:) show
		3 43 M (P:) show
	}ifelse
	grestore
}!
% string x y n accordn - accordion tablature
/accordn{
	-18 mul add barh add 3 add M showc
}!
/accordb{
	0 eq{20 add M 0 barh RL stroke}
	{pop pop}ifelse pop}!
%%endps

X:3
T:Andro à Patrice Corbel
N: validé 03/05/01 -- 04/08/02
R:Andro
O:Bretagne
M:4/4
K:Am
%%tablature 70 accordh accordn accordb
 "A"A2 cA "F" aAce | "G"dGBd "C"e4 | "F"fedc "G"B2 AB |1 "F"cABc "E"B4 :|2 "F"cABc "A"A4 :|\
w:    *  * * * * * *   8 6 7 8  7'   * * * *  * * *   * * * *  7   * * * *  *  
w:    7  8 7 11 7 8 9   * * * *  *   8' 9 7' 8  6' 7 6'   8 7 6' 8  *   8 7 6' 8  7  
 "A"A2 GF "E"E3 B | "E"c2 d2 "C"e4 | "F"fedc "G"B2 AB |1 "F"cABc "E"B4 :|2 "F"cABc "A"A4 :|
w:    * 6 *  4' 7   6' 8  7'   * * * *  * * *   * * * *  7   * * * *  *  
w:    7 * 4'  * *   * *  *   8' 9 7' 8  6' 7 6'   8 7 6' 8  *   8 7 6' 8  7  

X:51
T:Jeune fille de quinze ans (50-51-52)
T:Une fille de rose
N: rev. 01/06/01
R:hanter dro
C:trad
A:Bretagne
O:France
M:3/4
L:1/8
Q:100
K:Am
%%tablature 90 accordh accordn accordb
 |: "Am" ee/f/ "Am" ed "Am" c2   | "Am" ed/c/ "G" Bd "Am" cA | "Am" ee/f/ "Am" ed "Am" c2   |1 "Am" ed/c/ "G" BG "Em" E2 :|2 "Am" ed/c/ "G" BG "Am" A2 |
w:     * * *  * *  * | * * *  7 8  * * | * * *  * *  * | * * *  7 6  4'| * * *  7 6  *  
w:     9 9 8'  9 7'  8 | 9 7' 8  * *  8 7 | 9 9 8'  9 7'  8 | 9 7' 8  * *  * | 9 7' 8  * *  7  
w: A a *  A a A~~a | A a * G g E e | A a *  A a A~~a | A a * G g E~~e | A a * G g E~~e | 
 |: "Am" AA/B/ "Am" cd "C" e2 | "G" de/f/ "F" af "Am" e2 | "Am" cd/e/ "F" fe "G" d2 |1 "G" Bc/d/ "Em" cB "Em" cB :|2 "G" Bc/d/ "Em" cB "Am" A2 ||
w:     * * *  * *  7' | 8 7' *  * *  * | * * *  * *  8 | 7 6' 8  6' 7  6' 7 | 7 6' 8  6' 7  *  
w:     7 7 6'  8 7' * | * * 8'  9' 8'  9 | 8 7' 9  8' 9  * | * * *  * *  * * | * * *  * *  7  
w: A a * A a C~c | G g * F f A~a | A a * F f G~g | G g * E e E e | G g * E e A~a |  
abcm2ps-8.5.2/build.ninja0000644000175000017500000000537612462105233013300 0ustar  jefjef# rules for ninja (ninja-build)

VERSION = 8.5.2

#cflags = -g -O2 -Wall -pipe -I.
cflags = -g -Wall -pipe -I.
ldflags = -lm

rule cc
#  command = gcc $cflags -c $in -o $out
  command = clang $cflags -c $in -o $out
# -mcpu=iwmmxt
# -mcpu=iwmmxt2
# -mthumb -march=armv7-a

rule ld
#  command = cc $ldflags -o $out $in
  command = clang $ldflags -o $out $in

build abc2ps.o: cc abc2ps.c | config.h abcparse.h abc2ps.h
build abcparse.o: cc abcparse.c | config.h abcparse.h abc2ps.h
build buffer.o: cc buffer.c | config.h abcparse.h abc2ps.h
build deco.o: cc deco.c | config.h abcparse.h abc2ps.h
build draw.o: cc draw.c | config.h abcparse.h abc2ps.h
build format.o: cc format.c | config.h abcparse.h abc2ps.h
build front.o: cc front.c | config.h abcparse.h abc2ps.h slre.h
build glyph.o: cc glyph.c | config.h abcparse.h abc2ps.h
build music.o: cc music.c | config.h abcparse.h abc2ps.h
build parse.o: cc parse.c | config.h abcparse.h abc2ps.h
build slre.o: cc slre.c | slre.h
build subs.o: cc subs.c | config.h abcparse.h abc2ps.h
build svg.o: cc svg.c | config.h abcparse.h abc2ps.h
build syms.o: cc syms.c | config.h abcparse.h abc2ps.h

build abcm2ps: ld abc2ps.o abcparse.o buffer.o deco.o draw.o format.o front.o $
  glyph.o music.o parse.o slre.o subs.o svg.o syms.o

default abcm2ps

rule dist_tar
  command = ln -s . abcm2ps-$VERSION; $
   tar -zcvf $out $
  abcm2ps-$VERSION/Changes $
  abcm2ps-$VERSION/INSTALL $
  abcm2ps-$VERSION/Makefile $
  abcm2ps-$VERSION/Makefile.in $
  abcm2ps-$VERSION/README $
  abcm2ps-$VERSION/abc2ps.c $
  abcm2ps-$VERSION/abc2ps.h $
  abcm2ps-$VERSION/abcparse.c $
  abcm2ps-$VERSION/abcparse.h $
  abcm2ps-$VERSION/accordion.abc $
  abcm2ps-$VERSION/build.ninja $
  abcm2ps-$VERSION/buffer.c $
  abcm2ps-$VERSION/chinese.abc $
  abcm2ps-$VERSION/configure $
  abcm2ps-$VERSION/config.h $
  abcm2ps-$VERSION/config.h.in $
  abcm2ps-$VERSION/deco.c $
  abcm2ps-$VERSION/deco.abc $
  abcm2ps-$VERSION/draw.c $
  abcm2ps-$VERSION/flute.fmt $
  abcm2ps-$VERSION/format.c $
  abcm2ps-$VERSION/front.c $
  abcm2ps-$VERSION/glyph.c $
  abcm2ps-$VERSION/glyphs.abc $
  abcm2ps-$VERSION/landscape.fmt $
  abcm2ps-$VERSION/music.c $
  abcm2ps-$VERSION/musicfont.fmt $
  abcm2ps-$VERSION/newfeatures.abc $
  abcm2ps-$VERSION/options.txt $
  abcm2ps-$VERSION/parse.c $
  abcm2ps-$VERSION/sample.abc $
  abcm2ps-$VERSION/sample2.abc $
  abcm2ps-$VERSION/sample3.abc $
  abcm2ps-$VERSION/sample3.eps $
  abcm2ps-$VERSION/sample4.abc $
  abcm2ps-$VERSION/sample5.abc $
  abcm2ps-$VERSION/sample8.html $
  abcm2ps-$VERSION/slre.c $
  abcm2ps-$VERSION/slre.h $
  abcm2ps-$VERSION/subs.c $
  abcm2ps-$VERSION/svg.c $
  abcm2ps-$VERSION/syms.c $
  abcm2ps-$VERSION/voices.abc;$
   rm abcm2ps-$VERSION

build abcm2ps-$VERSION.tar.gz: dist_tar Changes

build dist: phony abcm2ps-$VERSION.tar.gz
abcm2ps-8.5.2/buffer.c0000644000175000017500000005203112452271204012564 0ustar  jefjef/*
 * Postscript buffering functions.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2015 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.
 */

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

#ifdef WIN32
#define snprintf _snprintf
#endif

#include "abc2ps.h" 

#define BUFFLN	80		/* max number of lines in output buffer */

static int ln_num;		/* number of lines in buffer */
static float ln_pos[BUFFLN];	/* vertical positions of buffered lines */
static char *ln_buf[BUFFLN];	/* buffer location of buffered lines */
static float ln_lmarg[BUFFLN];	/* left margin of buffered lines */
static float ln_scale[BUFFLN];	/* scale of buffered lines */
static signed char ln_font[BUFFLN];	/* font of buffered lines */
static float cur_lmarg = 0;	/* current left margin */
static float min_lmarg, max_rmarg;	/* margins for -E/-g */
static float cur_scale = 1.0;	/* current scale */
static float maxy;		/* remaining vertical space in page */
static float bposy;		/* current position in buffered data */
static int nepsf;		/* counter for -E/-g output files */
static int nbpages;		/* number of pages in the output file */
static int outbufsz;		/* size of outbuf */
static char outfnam[FILENAME_MAX]; /* internal file name for open/close */
static struct FORMAT *p_fmt;	/* current format while treating a new page */

int (*output)(FILE *out, const char *fmt, ...);

int in_page;			/* filling a PostScript page */
char *outbuf;			/* output buffer.. should hold one tune */
char *mbf;			/* where to a2b() */
int use_buffer;			/* 1 if lines are being accumulated */

/* -- cut off extension on a file identifier -- */
static void cutext(char *fid)
{
	char *p;

	if ((p = strrchr(fid, DIRSEP)) == NULL)
		p = fid;
	if ((p = strrchr(p, '.')) != NULL)
		*p = '\0';
}

/* -- open the output file -- */
void open_fout(void)
{
	int i;
	char fnm[FILENAME_MAX];

	strcpy(fnm, outfn);
	i = strlen(fnm) - 1;
	if (i < 0) {
		strcpy(fnm, svg || epsf > 1 ? "Out.xhtml" : OUTPUTFILE);
	} else if (i != 0 || fnm[0] != '-') {
		if (fnm[i] == '=' && in_fname) {
			char *p;

			if ((p = strrchr(in_fname, DIRSEP)) == NULL)
				p = in_fname;
			else
				p++;
			strcpy(&fnm[i], p);
			strext(fnm, svg || epsf > 1 ? "xhtml" : "ps");
		} else if (fnm[i] == DIRSEP) {
			strcpy(&fnm[i + 1],
				svg || epsf > 1 ? "Out.xhtml" : OUTPUTFILE);
		}
#if 0
/*fixme: fnm may be a directory*/
		else	...
#endif
	}
	if (svg == 1
	 && (i != 0 || fnm[0] != '-')) {
		cutext(fnm);
		i = strlen(fnm) - 1;
		if (strncmp(fnm, outfnam, i) != 0)
			nepsf = 0;
		sprintf(&fnm[i + 1], "%03d.svg", ++nepsf);
	} else if (strcmp(fnm, outfnam) == 0) {
		return;				/* same output file */
	}

	close_output_file();
	strcpy(outfnam, fnm);
	if (i != 0 || fnm[0] != '-') {
		if ((fout = fopen(fnm, "w")) == NULL) {
			error(1, NULL, "Cannot create output file %s - abort", fnm);
			exit(EXIT_FAILURE);
		}
	} else {
		fout = stdout;
	}
}

/* -- convert a date -- */
static void cnv_date(time_t *ltime)
{
	char buf[TEX_BUF_SZ];

	tex_str(cfmt.dateformat);
	strcpy(buf, tex_buf);
	strftime(tex_buf, TEX_BUF_SZ, buf, localtime(ltime));
}

/* initialize the min/max margin values */
/* (used only with eps -E and svg -g) */
void marg_init(void)
{
	min_lmarg = cfmt.pagewidth;
	max_rmarg = cfmt.pagewidth;
}

/* -- initialize the postscript file (PS or EPS) -- */
static void init_ps(char *str)
{
	time_t ltime;
	unsigned i;
	char version[32];

	if (epsf) {
		cur_lmarg = min_lmarg - 10;
		fprintf(fout, "%%!PS-Adobe-2.0 EPSF-2.0\n"
			"%%%%BoundingBox: 0 0 %.0f %.0f\n",
			(p_fmt->landscape ? p_fmt->pageheight : p_fmt->pagewidth)
				- cur_lmarg - max_rmarg + 10,
			-bposy);
		marg_init();
	} else {
		if (!fout)
			open_fout();
		fprintf(fout, "%%!PS-Adobe-2.0\n");
		fprintf(fout, "%%%%BoundingBox: 0 0 %.0f %.0f\n",
			p_fmt->pagewidth,
			p_fmt->pageheight);
	}
	fprintf(fout, "%%%%Title: %s\n", str);
	time(&ltime);
#ifndef WIN32
	strftime(tex_buf, TEX_BUF_SZ, "%b %e, %Y %H:%M", localtime(&ltime));
#else
	strftime(tex_buf, TEX_BUF_SZ, "%b %#d, %Y %H:%M", localtime(&ltime));
#endif
	fprintf(fout, "%%%%Creator: abcm2ps-" VERSION "\n"
		"%%%%CreationDate: %s\n", tex_buf);
	if (!epsf)
		fprintf(fout, "%%%%Pages: (atend)\n");
	fprintf(fout, "%%%%LanguageLevel: 3\n"
		"%%%%EndComments\n"
		"%%CommandLine:");
	for (i = 1; i < (unsigned) s_argc; i++) {
		char *p, *q;
		int space;

		p = s_argv[i];
		space = strchr(p, ' ') != NULL || strchr(p, '\n') != NULL;
		fputc(' ', fout);
		if (space)
			fputc('\'', fout);
		for (;;) {
			q = strchr(p, '\n');
			if (!q)
				break;
			fprintf(fout, " %.*s\n%%", (int) (q - p), p);
			p = q + 1;
		}
		fprintf(fout, "%s", p);
		if (space)
			fputc('\'', fout);
	}
	fprintf(fout, "\n\n");
	if (epsf)
		fprintf(fout, "save\n");
	strcpy(version, "/creator [(abcm2ps) " VERSION "] def");
	for (i = 0; i < strlen(version); i++) {
		if (version[i] == '.')
			version[i] = ' ';
	}
	fprintf(fout, "%%%%BeginSetup\n"
		"/!{bind def}bind def\n"
		"/bdef{bind def}!\n"		/* for compatibility */
		"/T/translate load def\n"
		"/M/moveto load def\n"
		"/RM/rmoveto load def\n"
		"/L/lineto load def\n"
		"/RL/rlineto load def\n"
		"/C/curveto load def\n"
		"/RC/rcurveto load def\n"
		"/SLW/setlinewidth load def\n"
		"/defl 0 def\n"	/* decoration flags - see deco.c for values */
		"/dlw{0.7 SLW}!\n"

		"%s\n", version);
	define_symbols();
	output = fprintf;
	user_ps_write();
	define_fonts();
	if (!epsf)
		fprintf(fout, "/setpagedevice where{pop\n"
			"	<</PageSize[%.0f %.0f]>>setpagedevice}if\n",
				p_fmt->pagewidth, p_fmt->pageheight);
	fprintf(fout, "%%%%EndSetup\n");
	file_initialized = 1;
}

/* -- initialize the svg file (option '-g') -- */
static void init_svg(char *str)
{
	cur_lmarg = min_lmarg - 10;
	output = svg_output;
#if 1 //fixme:test
	if (file_initialized > 0)
		fprintf(stderr, "??? init_svg: file_initialized\n");
#endif
	define_svg_symbols(str, nepsf,
		(p_fmt->landscape ? p_fmt->pageheight : p_fmt->pagewidth)
				- cur_lmarg - max_rmarg + 10,
		-bposy);
	file_initialized = 1;
	user_ps_write();
}

static void close_fout(void)
{
	long m;

	if (fout == stdout)
		goto out2;
	if (quiet)
		goto out1;
	m = ftell(fout);
	if (epsf || svg == 1)
		fprintf(stderr, "Output written on %s (%ld bytes)\n",
			outfnam, m);
	else
		fprintf(stderr,
			"Output written on %s (%d page%s, %d title%s, %ld bytes)\n",
			outfnam,
			nbpages, nbpages == 1 ? "" : "s",
			tunenum, tunenum == 1 ? "" : "s",
			m);
out1:
	fclose(fout);
out2:
	fout = NULL;
	file_initialized = 0;
}

/* -- close the output file -- */
/* epsf is always null */
void close_output_file(void)
{
	if (!fout)
		return;
	if (multicol_start != 0) {	/* if no '%%multicol end' */
		error(1, NULL, "No \"%%%%multicol end\"");
		multicol_start = 0;
		write_buffer();
	}
	if (tunenum == 0)
		error(0, NULL, "No tunes written to output file");
	close_page();
	switch (svg) {
	case 0:				/* PS */
		if (epsf == 0)
			fprintf(fout, "%%%%Trailer\n"
				"%%%%Pages: %d\n"
				"%%EOF\n", nbpages);
		close_fout();
		break;
	case 2:				/* -X */
		fputs("</body>\n"
			"</html>\n", fout);
	case 3:				/* -z */
		close_fout();
		break;
//	default:
//	case 1:				/* -v */
//		'fout' is closed in close_page
	}

	nbpages = tunenum = 0;
	defl = 0;
}

/* -- close the PS / SVG page -- */
void close_page(void)
{
	if (!in_page)
		return;
	in_page = 0;
	if (svg) {
		svg_close();
		if (svg == 1 && fout != stdout)
			close_fout();
//		else
//			fputs("</p>\n", fout);
	} else {
#if 1
		fprintf(fout, "grestore\n"
				"showpage\n"
				"%%%%EndPage: %d %d\n",
				nbpages, nbpages);
#else
		fputs("%%PageTrailer\n"
			"grestore\n"
			"showpage\n", fout);
#endif
	}
	cur_lmarg = 0;
	cur_scale = 1.0;
	outft = -1;
}

/* -- output a header/footer element -- */
static void format_hf(char *d, char *p)
{
	char *q;
	time_t ltime;

	for (;;) {
		if (*p == '\0')
			break;
		if ((q = strchr(p, '$')) != NULL)
			*q = '\0';
		d += sprintf(d, "%s", p);
		if (!q)
			break;
		p = q + 1;
		switch (*p) {
		case 'd':
			ltime = mtime;
			goto dput;
		case 'D':
			time(&ltime);
		dput:	cnv_date(&ltime);
			d += sprintf(d, "%s", tex_buf);
			break;
		case 'F':		/* ABC file name */
#if DIRSEP!='\\'
			d += sprintf(d, "%s", in_fname);
#else
			{
				int i;

				q = in_fname;
				i = TEX_BUF_SZ;
				for (;;) {
					if (--i <= 0 || *q == '\0')
						break;
					if ((*d++ = *q++) == '\\') {
						i--;
						*d++ = '\\';
					}
				}
				*d = '\0';
			}
#endif
			break;
		case 'I':		/* information field */
			p++;
			if (*p < 'A' || *p > 'Z' || !info[*p - 'A'])
				break;
			d += sprintf(d, "%s", &info[*p - 'A']->as.text[2]);
			break;
		case 'P':		/* page number */
			if (p[1] == '0') {
				p++;
				if (pagenum & 1)
					break;
			} else if (p[1] == '1') {
				p++;
				if ((pagenum & 1) == 0)
					break;
			}
			d += sprintf(d, "%d", pagenum);
			break;
		case 'T':		/* tune title */
			q = &info['T' - 'A']->as.text[2];
			tex_str(q);
			d += sprintf(d, "%s", tex_buf);
			break;
		case 'V':
			d += sprintf(d,"abcm2ps-"  VERSION);
			break;
		default:
			continue;
		}
		p++;
	}
	*d = '\0';		/* in case of empty string */
}

/* -- output the header or footer -- */
static float headfooter(int header,
			float pwidth,
			float pheight)
{
	char tmp[TEX_BUF_SZ], str[TEX_BUF_SZ + 1024];
	char *p, *q, *r, *mbf_sav;
	float size, y, wsize;
	struct FONTSPEC *f, f_sav;
	int cft_sav, dft_sav;

	if (header) {
		p = cfmt.header;
		f = &cfmt.font_tb[HEADERFONT];
		size = f->size;
		y = -size;
	} else {
		p = cfmt.footer;
		f = &cfmt.font_tb[FOOTERFONT];
		size = f->size;
		y = - (pheight - cfmt.topmargin - cfmt.botmargin)
			+ size;
	}
	if (*p == '-') {
		if (pagenum == 1)
			return 0;
		p++;
	}
	get_str_font(&cft_sav, &dft_sav);
	memcpy(&f_sav, &cfmt.font_tb[0], sizeof f_sav);
	str_font(f - cfmt.font_tb);
	output(fout, "%.1f F%d ", size, f->fnum);
	outft = f - cfmt.font_tb;

	/* may have 2 lines */
	wsize = size;
	if ((r = strstr(p, "\\n")) != NULL) {
		if (!header)
			y += size;
		wsize += size;
		*r = '\0';
	}
	mbf_sav = mbf;
	for (;;) {
		tex_str(p);
		strcpy(tmp, tex_buf);
		format_hf(str, tmp);

		/* left side */
		p = str;
		if ((q = strchr(p, '\t')) != NULL) {
			if (q != p) {
				*q = '\0';
				output(fout, "%.1f %.1f M ",
					p_fmt->leftmargin, y);
				str_out(p, A_LEFT);
				a2b("\n");
				mbf = mbf_sav;
				if (svg)
					svg_write(mbf, strlen(mbf));
				else
					fputs(mbf, fout);
			}
			p = q + 1;
		}
		if ((q = strchr(p, '\t')) != NULL)
			*q = '\0';

		/* center */
		if (q != p) {
			output(fout, "%.1f %.1f M ",
				pwidth * 0.5, y);
			str_out(p, A_CENTER);
			a2b("\n");
			mbf = mbf_sav;
			if (svg)
				svg_write(mbf, strlen(mbf));
			else
				fputs(mbf, fout);
		}

		/* right side */
		if (q) {
			p = q + 1;
			if (*p != '\0') {
				output(fout, "%.1f %.1f M ",
					pwidth - p_fmt->rightmargin, y);
				str_out(p, A_RIGHT);
				a2b("\n");
				mbf = mbf_sav;
				if (svg)
					svg_write(mbf, strlen(mbf));
				else
					fputs(mbf, fout);
			}
		}
		if (!r)
			break;
		*r = '\\';
		p = r + 2;
		r = NULL;
		y -= size;
	}

	/* restore the fonts */
	*mbf_sav = '\0';
	memcpy(&cfmt.font_tb[0], &f_sav, sizeof cfmt.font_tb[0]);
	set_str_font(cft_sav, dft_sav);
	return wsize;
}

/* -- initialize the first page or a new page for svg -- */
/* the flag 'in_page' is always false and epsf is always null */
static void init_page(void)
{
	float pheight, pwidth;

	p_fmt = !info['X' - 'A'] ? &cfmt : &dfmt;	/* global format */

	nbpages++;
	if (svg) {
		if (file_initialized <= 0) {
			if (!fout)
				open_fout();
			define_svg_symbols(in_fname, nbpages,
				cfmt.landscape ? p_fmt->pageheight : p_fmt->pagewidth,
				cfmt.landscape ? p_fmt->pagewidth : p_fmt->pageheight);
			file_initialized = 1;
			output = svg_output;
		} else {
			define_svg_symbols(in_fname, nbpages,
				cfmt.landscape ? p_fmt->pageheight : p_fmt->pagewidth,
				cfmt.landscape ? p_fmt->pagewidth : p_fmt->pageheight);
		}
		user_ps_write();
	} else if (file_initialized <= 0) {
		init_ps(in_fname);
	}
	in_page = 1;
	outft = -1;

	if (!svg)
		fprintf(fout, "%%%%Page: %d %d\n",
			nbpages, nbpages);
	if (cfmt.landscape) {
		pheight = p_fmt->pagewidth;
		pwidth = cfmt.pageheight;
		if (!svg)
			fprintf(fout, "%%%%PageOrientation: Landscape\n"
				"gsave 90 rotate 0 %.1f T\n",
				-cfmt.topmargin);
	} else {
		pheight = cfmt.pageheight;
		pwidth = p_fmt->pagewidth;
		if (!svg)
			fprintf(fout, "gsave 0 %.1f T\n",
				pheight - cfmt.topmargin);
	}
	if (svg)
		output(fout, "0 %.1f T\n", -cfmt.topmargin);
	else
		output(fout,
			"%% --- width %.1f\n",		/* for index */
			((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
			 - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale);

	maxy = pheight - cfmt.topmargin - cfmt.botmargin;

	/* output the header and footer */
	if (!cfmt.header) {
		char *p = NULL;

		switch (pagenumbers) {
		case 1: p = "$P\t"; break;
		case 2: p = "\t\t$P"; break;
		case 3: p = "$P0\t\t$P1"; break;
		case 4: p = "$P1\t\t$P0"; break;
		}
		if (p)
			cfmt.header = strdup(p);
	}
	if (cfmt.header) {
		float dy;

		dy = headfooter(1, pwidth, pheight);
		if (dy != 0) {
			output(fout, "0 %.1f T\n", -dy);
			maxy -= dy;
		}
	}
	if (cfmt.footer)
		maxy -= headfooter(0, pwidth, pheight);
	pagenum++;
	outft = -1;
}

/* -- adjust the tune title part of the output file name -- */
static void epsf_fn_adj(char *p)
{
	char c;

	while ((c = *p) != '\0') {
		if (c == ' ')
			*p = '_';
		else if (c == DIRSEP || (unsigned) c >= 127)
			*p = '.';
		p++;
	}
}

/* -- build the title of the eps/svg file and check if correct utf-8 -- */
static void epsf_title(char *p, int sz)
{
	unsigned char c;

	snprintf(p, sz, "%.72s (%.4s)", in_fname, &info['X' - 'A']->as.text[2]);
	while ((c = (unsigned char) *p) != '\0') {
		if (c >= 0x80) {
			if ((c & 0xf8) == 0xf0) {
				if ((p[1] & 0xc0) != 0x80
				 && (p[2] & 0xc0) != 0x80
				 && (p[3] & 0xc0) != 0x80)
					*p = ' ';
			} else if ((c & 0xf0) == 0xe0) {
				if ((p[1] & 0xc0) != 0x80
				 && (p[2] & 0xc0) != 0x80)
					*p = ' ';
			} else if ((c & 0xe0) == 0xc0) {
				if ((p[1] & 0xc0) != 0x80)
					*p = ' ';
			} else {
				*p = ' ';
			}
		}
		p++;
	}
}

/* -- output a EPS (-E) or SVG (-g) file -- */
void write_eps(void)
{
	unsigned i;
	char *p, title[80];

	if (mbf == outbuf
	 || !info['X' - 'A'])
		return;

	p_fmt = &cfmt;				/* tune format */

	if (epsf != 3) {			/* if not -z */
		strcpy(outfnam, outfn);
		if (outfnam[0] == '\0')
			strcpy(outfnam, OUTPUTFILE);
		cutext(outfnam);
		i = strlen(outfnam) - 1;
		if (i == 0 && outfnam[0] == '-') {
			if (epsf == 1) {
				error(1, NULL, "Cannot use stdout with '-E' - abort");
				exit(EXIT_FAILURE);
			}
			fout = stdout;
		} else {
			if (outfnam[i] == '=') {
				p = &info['T' - 'A']->as.text[2];
				while (isspace((unsigned char) *p))
					p++;
				strncpy(&outfnam[i], p, sizeof outfnam - i - 4);
				outfnam[sizeof outfnam - 5] = '\0';
				epsf_fn_adj(&outfnam[i]);
			} else {
				if (i >= sizeof outfnam - 4 - 3)
					i = sizeof outfnam - 4 - 3;
				sprintf(&outfnam[i + 1], "%03d", ++nepsf);
			}
			strcat(outfnam, epsf == 1 ? ".eps" : ".svg");
			if ((fout = fopen(outfnam, "w")) == NULL) {
				error(1, NULL, "Cannot open output file %s - abort",
						outfnam);
				exit(EXIT_FAILURE);
			}
		}
	}
	epsf_title(title, sizeof title);
	if (epsf == 1) {
		init_ps(title);
		fprintf(fout, "0 %.1f T\n", -bposy);
		write_buffer();
		fprintf(fout, "showpage\nrestore\n");
	} else {
		if (epsf == 3 && file_initialized == 0)
			fputs("<br/>\n", fout);	/* new image in the same flow */
		init_svg(title);
		write_buffer();
		svg_close();
	}
	if (epsf != 3)
		close_fout();
	else
		file_initialized = 0;
	cur_lmarg = 0;
	cur_scale = 1.0;
}

/* -- start a new page -- */
/* epsf is always null */
static void newpage(void)
{
	close_page();
	init_page();
}

/*  subroutines to handle output buffer  */

/* -- update the output buffer pointer -- */
void a2b(char *fmt, ...)
{
	va_list args;

	if (mbf + BSIZE > outbuf + outbufsz) {
		if (epsf) {
			error(1, NULL, "Output buffer overflow - increase outbufsz");
			fprintf(stderr, "*** abort\n");
			exit(EXIT_FAILURE);
		}
		error(0, NULL, "Possible buffer overflow");
		write_buffer();
		use_buffer = 0;
	}
	va_start(args, fmt);
	mbf += vsnprintf(mbf, outbuf + outbufsz - mbf, fmt, args);
	va_end(args);
}

/* -- translate down by 'h' scaled points in output buffer -- */
void bskip(float h)
{
	if (h == 0)
		return;
	bposy -= h * cfmt.scale;
	a2b("0 %.2f T\n", -h);
}

/* -- initialize the output buffer -- */
void init_outbuf(int kbsz)
{
	if (outbuf)
		free(outbuf);
	outbufsz = kbsz * 1024;
	if (outbufsz < 0x10000)
		outbufsz = 0x10000;
	outbuf = malloc(outbufsz);
	if (!outbuf) {
		error(1, NULL, "Out of memory for outbuf - abort");
		exit(EXIT_FAILURE);
	}
	bposy = 0;
	ln_num = 0;
	mbf = outbuf;
}

/* -- write buffer contents, break at full pages -- */
void write_buffer(void)
{
	char *p_buf;
	int l, np;
	float p1, dp;
	int outft_sav;

	if (mbf == outbuf || multicol_start != 0)
		return;
	if (!in_page && !epsf)
		init_page();
	outft_sav = outft;
	p1 = 0;
	p_buf = outbuf;
	for (l = 0; l < ln_num; l++) {
		if (ln_pos[l] > 0) {		/* if in multicol */
			int ll;
			float pos;

			for (ll = l + 1; ll < ln_num; ll++) {
				if (ln_pos[ll] <= 0) {
					pos = ln_pos[ll];
					while (--ll >= l)
						ln_pos[ll] = pos;
					break;
				}
			}
		}
		dp = ln_pos[l] - p1;
		np = maxy + dp < 0 && !epsf;
		if (np) {
			newpage();
			if (ln_font[l] >= 0) {
				struct FONTSPEC *f;

				f = &cfmt.font_tb[ln_font[l]];
				output(fout, "%.1f F%d\n",
					f->size, f->fnum);
			}
		}
		if (ln_scale[l] != cur_scale) {
			output(fout, "%.3f dup scale\n",
				ln_scale[l] / cur_scale);
			cur_scale = ln_scale[l];
		}
		if (ln_lmarg[l] != cur_lmarg) {
			output(fout, "%.2f 0 T\n",
				(ln_lmarg[l] - cur_lmarg) / cur_scale);
			cur_lmarg = ln_lmarg[l];
		}
		if (np) {
			output(fout, "0 %.2f T\n", -cfmt.topspace);
			maxy -= cfmt.topspace * cfmt.scale;
		}
		if (*p_buf != '\001') {
			if (epsf > 1 || svg)
				svg_write(p_buf, ln_buf[l] - p_buf);
			else
				fwrite(p_buf, 1, ln_buf[l] - p_buf, fout);
		} else {			/* %%EPS - see parse.c */
			FILE *f;
			char line[BSIZE], *p, *q;

			p = strchr(p_buf + 1, '\n');
			fwrite(p_buf + 1, 1, p - p_buf, fout);
			p_buf = p + 1;
			p = strchr(p_buf, '%');
			*p++ = '\0';
			q = strchr(p, '\n');
			*q = '\0';
			if ((f = fopen(p, "r")) == NULL) {
				error(1, NULL, "Cannot open EPS file '%s'", p);
			} else {
				if (epsf > 1 || svg) {
					fprintf(fout, "<!--Begin document %s-->\n",
							p);
					svg_output(fout, "gsave\n"
							"%s T\n",
							p_buf);
					while (fgets(line, sizeof line, f))	/* copy the file */
						svg_write(line, strlen(line));
					svg_output(fout, "grestore\n"
							"%s T\n",
							p_buf);
					fprintf(fout, "<!--End document %s-->\n",
							p);
				} else {
					fprintf(fout,
						"save\n"
						"/showpage{}def/setpagedevice{pop}def\n"
						"%s T\n"
						"%%%%BeginDocument: %s\n",
						p_buf, p);
					while (fgets(line, sizeof line, f))	/* copy the file */
						fwrite(line, 1, strlen(line), fout);
					fprintf(fout, "%%%%EndDocument\n"
							"restore\n");
				}
				fclose(f);
			}
		}
		p_buf = ln_buf[l];
		maxy += dp;
		p1 = ln_pos[l];
	}
#if 1 //fixme:test
	if (*p_buf != '\0')
		fprintf(stderr, "??? bug - buffer not empty:\n%s\n", p_buf);
#endif
	outft = outft_sav;
	bposy = 0;
	ln_num = 0;
	mbf = outbuf;
}

/* -- add a block in the output buffer -- */
void block_put(void)
{
	if (mbf == outbuf)
		return;
//fixme: should be done sooner and should be adjusted when cfmt change...
	if (maxy == 0)
		maxy = (cfmt.landscape ? cfmt.pagewidth : cfmt.pageheight)
			- cfmt.topmargin - cfmt.botmargin;
	if (ln_num > 0 && mbf == ln_buf[ln_num - 1])
		return;				/* no data */
	if (ln_num >= BUFFLN) {
		char c, *p;

		error(1, NULL, "max number of buffer lines exceeded"
				" -- check BUFFLN");
		multicol_start = 0;
		p = ln_buf[ln_num - 1];
		c = *p;				/* (avoid "buffer not empty") */
		*p = '\0';
		write_buffer();
		multicol_start = maxy + bposy;
		*p = c;
		strcpy(outbuf, p);
		use_buffer = 0;
	}
	ln_buf[ln_num] = mbf;
	ln_pos[ln_num] = multicol_start == 0 ? bposy : 1;
	ln_lmarg[ln_num] = cfmt.leftmargin;
	if (epsf) {
		if (cfmt.leftmargin < min_lmarg)
			min_lmarg = cfmt.leftmargin;
		if (cfmt.rightmargin < max_rmarg)
			max_rmarg = cfmt.rightmargin;
	}
	ln_scale[ln_num] = cfmt.scale;
	ln_font[ln_num] = outft;
	ln_num++;

	if (!use_buffer)
		write_buffer();
}

/* -- handle completed block in buffer -- */
/* if the added stuff does not fit on current page, write it out
   after page break and change buffer handling mode to pass though */
void buffer_eob(void)
{
	block_put();
	if (maxy + bposy < 0
	 && !epsf
	 && multicol_start == 0) {
		if (in_page)
			newpage();
		write_buffer();
		use_buffer = 0;
	}
	if (epsf == 3)
		write_eps();		/* close the image */
}

/* -- dump buffer if not enough place for a music line -- */
void check_buffer(void)
{
	if (mbf + 5000 > outbuf + outbufsz) { /* assume music line < 5000 bytes */
		error(0, NULL,
		      "Possibly bad page breaks, outbufsz exceeded");
		write_buffer();
		use_buffer = 0;
	}
}

/* -- return the current vertical offset in the page -- */
float get_bposy(void)
{
	return maxy + bposy;
}
abcm2ps-8.5.2/chinese.abc0000644000175000017500000000446212453514465013253 0ustar  jefjef% abcm2ps sample file with chinese characters

% this file works with truetype fonts (as UKaiCN) and pango support in abcm2ps

X: 1
T: Green Island Serenade
T: 绿岛小夜曲
T: 綠島小夜曲
T: Lǜ Dǎo Xiǎoyèqǔ
T: Зелёный Остров
T: جزيرة اخضرة
C: Chen Chang-shou
D: Vienna Teng "Warm Strangers"
M: 4/4
L: 1/8
K: D
A,2 \
| "D"A3 B "(F#m)"A2 F2 | "A7"E2 {FE}DE "Bm"F3 E \
| "G"DD2 B, "D"A,3 B, | "D"A,4 "(A7)"z4 \
| "G"B,3 A, B,2 D2 | "A7"E2 {FE}DE "D"F3 A \
| "Em"EE2 F "A7"A3 B | "D"A4 "(A7)"z4 |
| "G"BB2 A "D"F2 FE | "Bm"DE F[AD] "F#m"F4 \
| "Em"EE2 D "G"B,A, B,D | "A7"E4 z4 \
| "D"FF2 E "Bm"FA F2 | "Em"EF ED "G"B,4 \
| "D"A,A FE "A7"AB FE | "D"{E}D4 z4 |]
[|"G"B>B BA "D"F F3 | "Em"EE2 D "G"B,A, B,D | "A7"E4 z4 \
| "D"A>B AE "F#m"FF2 F | "Em"EE2 D "G"B,2 D2 | "D"A4 "A7"z3A |
| "D"A2 {BA}FA "G"B3 d | "D"AB AF "Em"E4 \
| "Bm"F2 ED "G"B,3 D | "A7"E2 {FE}DE "D"F3A \
| "G"B3 F "A7"E3 F/E/ | "D"D4 z2 |]
%
W: 这绿岛像一隻船在月夜里摇呀摇
W: 姑娘哟妳也在我的心海裡飘呀飘
W: 让我的歌声随那微风吹開了妳的窗簾
W: 让我的衷情随那流水不断的向妳倾诉
W:
W: 椰子树的长影掩不住我的情意
W: 明媚的月光更照亮了我的心
W: 这绿岛的夜已经这样沉静
W: 姑娘哟妳为什么还是默默无语
W:
W: zhè lǜ dǎo xiàng yī zhī chuàn, zài yué yè lǐ yáo ya yáo.
W: gū niang yo, nǐ ye zài wǒde xīn hǎi lǐ piao ya piao.
W: ràng wǒde ge sheng sui na wei feng, chui kai le nǐ de chuang lian.
W: ràng wǒde zhong qing sui na liu shui, bu duan de xiang nǐ qing su.
W:
W: ye zi shu de chang ying yan bu zhu wǒde qing yi;
W: mìng mèi de yuè guang geng zhao liang le wǒde xīn.
W: zhe lǜ dao de ye yi jing zhe yang de chen jing
W: gū niang ya, nǐ wei shen ma hai shì muò muò wú yǚ?
W:
W: This Green Island is like a boat, floating in the moonlight,
W: My darling, you too are floating in the sea of my heart,
W: Let the sound of my song follow the breeze, blowing open the curtain of your window,
W: Let my love follow the flowing water, endlessly pouring out its feelings for you.
W:
W: The long shadows of the palm trees cannot conceal my love,
W: The bright beauty of the moonlight casts its brilliance into my heart.
W: This Green Island night is so calm and serene,
W: My darling, why are you silent, saying nothing?
abcm2ps-8.5.2/configure0000755000175000017500000000313412462105175013062 0ustar  jefjef#! /bin/sh

### custom variables ###

CC=gcc
CFLAGS="-g -O2 -Wall -pipe"

INSTALL="/usr/bin/install -c"
INSTALL_DATA='${INSTALL} -m 644'
INSTALL_PROGRAM='${INSTALL}'

prefix=/usr/local
exec_prefix='${prefix}'
srcdir=.
bindir='${exec_prefix}/bin'
libdir='${exec_prefix}/lib'
datarootdir='${prefix}/share'
docdir='${prefix}/doc'
DEFAULT_FDIR="$prefix/share/abcm2ps"

### custom end ###

if test -f ./custom; then
	. ./custom
fi

VERSION=8.5.2
VDATE='January 28, 2015'

case "$1" in
	--*=*)
		cmd=${1#--}
		eval $cmd
		;;
esac

if which pkg-config > /dev/null ; then
	if pkg-config --exists freetype2 ; then
		if pkg-config --exists pangocairo ; then
			CPPFLAGS="-DHAVE_PANGO=1"
			CPPPANGO="`pkg-config pango cairo freetype2 --cflags`"
			LDFLAGS="$LDFLAGS `pkg-config pangocairo pangoft2 freetype2 --libs`"
		else
			echo "pangocairo not found - no pango support"
		fi
	else
		echo "freetype2 not found - no pango support"
	fi
else
	echo "pkg-config not found - no pango support"
fi

sed "s+@CC@+$CC+
s+@CPPFLAGS@+$CPPFLAGS+
s+@CPPPANGO@+$CPPPANGO+
s+@CFLAGS@+$CFLAGS+
s+@LDFLAGS@+$LDFLAGS+
s+@INSTALL@+$INSTALL+
s+@INSTALL_DATA@+$INSTALL_DATA+
s+@INSTALL_PROGRAM@+$INSTALL_PROGRAM+
s+@prefix@+$prefix+
s+@exec_prefix@+$exec_prefix+
s+@srcdir@+$srcdir+
s+@bindir@+$bindir+
s+@libdir@+$libdir+
s+@datarootdir@+$datarootdir+
s+@docdir@+$docdir+
s+@VERSION@+$VERSION+" Makefile.in > Makefile
echo "Makefile created"

sed "s/define VERSION xxx/\define VERSION \"$VERSION\"/
s/define VDATE xxx/define VDATE \"$VDATE\"/
s+define DEFAULT_FDIR xxx+define DEFAULT_FDIR \"$DEFAULT_FDIR\"+
" config.h.in > config.h
echo "config.h created"
abcm2ps-8.5.2/config.h0000644000175000017500000000060512462105211012560 0ustar  jefjef/* config.h.in */

/* uncomment to handle the european A4 format.  */
//#define A4_FORMAT 1

/* uncomment to have ~ as roll instead of twiddle.  */
//#define DECO_IS_ROLL 1

/* comment if you have not mmap() */
#define HAVE_MMAP 1

/* default directory to search for format files */
#define DEFAULT_FDIR "/usr/local/share/abcm2ps"

#define VERSION "8.5.2"
#define VDATE "January 28, 2015"
abcm2ps-8.5.2/config.h.in0000644000175000017500000000053312316560003013167 0ustar  jefjef/* config.h.in */

/* uncomment to handle the european A4 format.  */
//#define A4_FORMAT 1

/* uncomment to have ~ as roll instead of twiddle.  */
//#define DECO_IS_ROLL 1

/* comment if you have not mmap() */
#define HAVE_MMAP 1

/* default directory to search for format files */
#define DEFAULT_FDIR xxx

#define VERSION xxx
#define VDATE xxx
abcm2ps-8.5.2/deco.c0000644000175000017500000014563412461656022012246 0ustar  jefjef/*
 * Decoration handling.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 2000-2015, Jean-François Moine.
 *
 * 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.
 */

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

#ifdef WIN32
#define lroundf(x) ((long) ((x) + 0.5))
#endif

#include "abc2ps.h"

int defl;		/* decoration flags */
char *deco[256];	/* decoration names */

static struct deco_elt {
	struct deco_elt *next, *prev;	/* next/previous decoration */
	struct SYMBOL *s;	/* symbol */
	struct deco_elt *start;	/* start a long decoration ending here */
	unsigned char t;	/* decoration index */
	unsigned char staff;	/* staff */
	unsigned char flags;
#define DE_VAL	0x01		/* put extra value if 1 */
#define DE_UP	0x02		/* above the staff */
#define DE_BELOW 0x08		/* below the staff */
#define DE_GRACE 0x10		/* in grace note */
#define DE_INV 0x20		/* invert the glyph */
#define DE_LDST 0x40		/* start of long decoration */
#define DE_LDEN 0x80		/* end of long decoration */
	unsigned char defl;	/* decorations flags - see DEF_xx */
	float x, y;		/* x, y */
	float dy;		/* dy for annotation strings */
	float v;		/* extra value */
	char *str;		/* string / 0 */
} *deco_head, *deco_tail;

typedef void draw_f(struct deco_elt *de);
static draw_f d_arp, d_cresc, d_near, d_slide, d_upstaff, d_pf, d_trill;

/* decoration table */
/* !! don't change the order of the numbered items !! */
static struct deco_def_s {
	char *name;
	unsigned char func;	/* function index */
	signed char ps_func;	/* postscript function index */
	unsigned char h;	/* height */
	unsigned char wl, wr;	/* left and right widths */
	unsigned char strx;	/* string index - 255=deco name */
	unsigned char ld_end;	/* index of end of long decoration */
	unsigned char flags;	/* only DE_LDST and DE_LDEN */
} deco_def_tb[128];

/* c function table */
static draw_f *func_tb[] = {
	d_near,		/* 0 - near the note */
	d_slide,	/* 1 */
	d_arp,		/* 2 */
	d_upstaff,	/* 3 - tied to note */
	d_upstaff,	/* 4 (below the staff) */
	d_trill,	/* 5 */
	d_pf,		/* 6 - tied to staff (dynamic marks) */
	d_cresc,	/* 7 */
};

/* postscript function table */
static char *ps_func_tb[128];

static char *str_tb[32];

/* standard decorations */
static char *std_deco_tb[] = {
	"dot 0 stc 5 1 1",
	"roll 3 cpu 7 6 6",
	"fermata 3 hld 12 7 7",
	"emphasis 3 accent 7 4 4",
	"lowermordent 3 lmrd 10 2 2",
	"coda 3 coda 24 10 10",
	"uppermordent 3 umrd 10 2 2",
	"segno 3 sgno 20 4 4",
	"trill 3 trl 11 4 4",
	"upbow 3 upb 10 5 5",
	"downbow 3 dnb 9 5 5",
	"gmark 3 grm 6 5 5",
	"slide 1 sld 3 7 0",
	"tenuto 0 emb 5 2 2",
	"breath 3 brth 0 1 20",
	"longphrase 3 lphr 0 1 1",
	"mediumphrase 3 mphr 0 1 1",
	"shortphrase 3 sphr 0 1 1",
	"invertedfermata 3 hld 12 7 7",
	"invertedturn 3 turn 10 0 5",
	"invertedturnx 3 turnx 10 0 5",
	"0 3 fng 8 3 3 0",
	"1 3 fng 8 3 3 1",
	"2 3 fng 8 3 3 2",
	"3 3 fng 8 3 3 3",
	"4 3 fng 8 3 3 4",
	"5 3 fng 8 3 3 5",
	"plus 3 dplus 7 3 3",
	"+ 3 dplus 7 3 3",
	"accent 3 accent 7 4 4",
	"> 3 accent 7 4 4",
	"marcato 3 marcato 9 3 3",
	"^ 3 marcato 9 3 3",
	"D.C. 3 dacs 16 10 10 D.C.",
	"D.S. 3 dacs 16 10 10 D.S.",
	"fine 3 dacs 16 10 10 FINE",
	"f 6 pf 18 1 7",
	"ff 6 pf 18 2 10",
	"fff 6 pf 18 4 13",
	"ffff 6 pf 18 6 16",
	"mf 6 pf 18 6 13",
	"mp 6 pf 18 6 16",
	"mordent 3 lmrd 10 2 2",
	"open 3 opend 10 2 2",
	"p 6 pf 18 2 8",
	"pp 6 pf 18 5 14",
	"ppp 6 pf 18 8 20",
	"pppp 6 pf 18 10 25",
	"pralltriller 3 umrd 10 2 2",
	"sfz 6 sfz 18 4 10",
	"turn 3 turn 10 0 5",
	"wedge 3 wedge 8 1 1",
	"turnx 3 turnx 10 0 5",
	"trill( 5 ltr 8 0 0",
	"trill) 5 ltr 8 0 0",
	"snap 3 snap 14 3 3",
	"thumb 3 thumb 14 2 2",
	"arpeggio 2 arp 12 10 0",
	"crescendo( 7 cresc 18 0 0",
	"crescendo) 7 cresc 18 0 0",
	"<( 7 cresc 18 0 0",
	"<) 7 cresc 18 0 0",
	"diminuendo( 7 dim 18 0 0",
	"diminuendo) 7 dim 18 0 0",
	">( 7 dim 18 0 0",
	">) 7 dim 18 0 0",
	"invisible 32 0 0 0 0",
	"beamon 33 0 0 0 0",
	"trem1 34 0 0 0 0",
	"trem2 34 0 0 0 0",
	"trem3 34 0 0 0 0",
	"trem4 34 0 0 0 0",
	"xstem 35 0 0 0 0",
	"beambr1 36 0 0 0 0",
	"beambr2 36 0 0 0 0",
	"rbstop 37 0 0 0 0",
	"/ 38 0 0 6 6",
	"// 38 0 0 6 6",
	"/// 38 0 0 6 6",
	"beam-accel 39 0 0 0 0",
	"beam-rall 39 0 0 0 0",
	"stemless 40 0 0 0 0",
	0
};

/* user decorations */
static struct u_deco {
	struct u_deco *next;
	char text[2];
} *user_deco;

static struct SYMBOL *first_note;	/* first note/rest of the line */

static void draw_gchord(struct SYMBOL *s, float gchy_min, float gchy_max);

/* -- get the max/min vertical offset -- */
float y_get(int staff,
		int up,
		float x,
		float w)
{
	struct STAFF_S *p_staff;
	int i, j;
	float y;

	p_staff = &staff_tb[staff];
	i = (int) (x / realwidth * YSTEP);
	if (i < 0) {
//		fprintf(stderr, "y_get i:%d\n", i);
		i = 0;
	}
	j = (int) ((x + w) / realwidth * YSTEP);
	if (j >= YSTEP) {
		j = YSTEP - 1;
		if (i > j)
			i = j;
	}
	if (up) {
		y = p_staff->top[i++];
		while (i <= j) {
			if (y < p_staff->top[i])
				y = p_staff->top[i];
			i++;
		}
	} else {
		y = p_staff->bot[i++];
		while (i <= j) {
			if (y > p_staff->bot[i])
				y = p_staff->bot[i];
			i++;
		}
	}
	return y;
}

/* -- adjust the vertical offsets -- */
void y_set(int staff,
		int up,
		float x,
		float w,
		float y)
{
	struct STAFF_S *p_staff;
	int i, j;

	p_staff = &staff_tb[staff];
	i = (int) (x / realwidth * YSTEP);
	/* (may occur when annotation on 'y' at start of an empty staff) */
	if (i < 0) {
//		fprintf(stderr, "y_set i:%d\n", i);
		i = 0;
	}
	j = (int) ((x + w) / realwidth * YSTEP);
	if (j >= YSTEP) {
		j = YSTEP - 1;
		if (i > j)
			i = j;
	}
	if (up) {
		while (i <= j) {
			if (p_staff->top[i] < y)
				p_staff->top[i] = y;
			i++;
		}
	} else {
		while (i <= j) {
			if (p_staff->bot[i] > y)
				p_staff->bot[i] = y;
			i++;
		}
	}
}

// set the string of a decoration
static char *set_str(struct deco_elt *de, char *str)
{
	float dx, dy;
	int n;

	if (sscanf(str, "@%f,%f%n", &dx, &dy, &n) == 2) {
		de->x += dx;
		de->dy = dy;
		return str + n;
	}
	return str;
}

/* -- get the staff position of the dynamic and volume marks -- */
static int up_p(struct SYMBOL *s, int pos)
{
	switch (pos) {
	case SL_ABOVE:
		return 1;
	case SL_BELOW:
		return 0;
	}
	if (s->multi != 0)
		return s->multi > 0;
	if (!voice_tb[s->voice].have_ly)
		return 0;

	/* above if the lyrics are below the staff */
	return s->posit.voc != SL_ABOVE;
}

/* -- drawing functions -- */
/* special case for arpeggio */
static void d_arp(struct deco_elt *de)
{
	struct SYMBOL *s;
	struct deco_def_s *dd;
	int m, h;
	float xc, dx;

	s = de->s;
	dd = &deco_def_tb[de->t];
	xc = 0;
	for (m = 0; m <= s->nhd; m++) {
		if (s->as.u.note.accs[m]) {
			dx = 5 + s->shac[m];
		} else {
			dx = 6 - s->shhd[m];
			switch (s->head) {
			case H_SQUARE:
			case H_OVAL:
				dx += 2.5;
				break;
			}
		}
		if (dx > xc)
			xc = dx;
	}
	h = 3 * (s->pits[s->nhd] - s->pits[0]) + 4;
	m = dd->h;		/* minimum height */
	if (h < m)
		h = m;

	de->flags |= DE_VAL;
	de->v = h;
	de->x = s->x - xc;
	de->y = (float) (3 * (s->pits[0] - 18)) - 3;
}

/* special case for crescendo/diminuendo */
static void d_cresc(struct deco_elt *de)
{
	struct SYMBOL *s, *s2;
	struct deco_def_s *dd, *dd2;
	struct deco_elt *de1;
	int up;
	float x, dx, x2;

	if (de->flags & DE_LDST)
		return;
	s2 = de->s;
	de1 = de->start;		/* start of the deco */
	if (de1) {
		s = de1->s;
		x = s->x + 3;
	} else {			/* end without start */
		s = first_note;
		x = s->x - s->wl - 4;
	}
	de->staff = s2->staff;
	de->flags &= ~DE_LDEN;		/* old behaviour */
	de->flags |= DE_VAL;
	up = up_p(s2, s2->posit.dyn);
	if (up)
		de->flags |= DE_UP;

	/* shift the starting point if any dynamic mark on the left */
	if (de1 && de1->prev && de1->prev->s == s
	 && ((de->flags ^ de1->prev->flags) & DE_UP) == 0) {
		dd2 = &deco_def_tb[de1->prev->t];
		if (dd2->func >= 6) {
			x2 = de1->prev->x + de1->prev->v + 4;
			if (x2 > x)
				x = x2;
		}
	}

	if (de->defl & DEF_NOEN) {	/* if no decoration end */
		dx = de->x - x;
		if (dx < 20) {
			x = de->x - 20 - 3;
			dx = 20;
		}
	} else {
		x2 = s2->x;
		if (de->next && de->next->s == s
		 && ((de->flags ^ de->next->flags) & DE_UP) == 0) {
			dd2 = &deco_def_tb[de->next->t];
			if (dd2->func >= 6)	/* if dynamic mark */
				x2 -= 5;
		}
		dx = x2 - x - 4;
		if (dx < 20) {
			x -= (20 - dx) * 0.5;
			if (!de->start)
				x -= (20 - dx) * 0.5;
			dx = 20;
		}
	}

	de->v = dx;
	de->x = x;
	dd = &deco_def_tb[de->t];
	de->y = y_get(de->staff, up, x, dx);
	if (!up)
		de->y -= dd->h;
	/* (y_set is done later in draw_deco_staff) */
}

/* near the note (dot, tenuto) */
static void d_near(struct deco_elt *de)
{
	struct SYMBOL *s;
	struct deco_def_s *dd;
	int y, up;

	s = de->s;
	dd = &deco_def_tb[de->t];
	if (s->multi)
		up = s->multi > 0;
	else
		up = s->stem < 0;
	if (up)
		y = s->ymx;
	else
		y = s->ymn - dd->h;
	if (y > -6 && y < 24) {
		if (up)
			y += 3;
		y = (y + 6) / 6 * 6 - 6;	/* between lines */
	}
	if (up)
		s->ymx = y + dd->h;
	else
		s->ymn = y;
	de->y = (float) y;
	de->x = s->x + s->shhd[s->stem >= 0 ? 0 : s->nhd];
	if (dd->name[0] == 'd'			/* if dot decoration */
	 && s->nflags >= -1) {			/* on stem */
		if (up) {
			if (s->stem > 0)
				de->x += STEM_XOFF;
		} else {
			if (s->stem < 0)
				de->x -= STEM_XOFF;
		}
	}
}

/* special case for piano/forte indications */
static void d_pf(struct deco_elt *de)
{
	struct SYMBOL *s;
	struct deco_def_s *dd, *dd2;
	float x, x2;
	char *str;
	int up;

	s = de->s;
	dd = &deco_def_tb[de->t];

	de->v = dd->wl + dd->wr;

	up = up_p(s, s->posit.vol);
	if (up)
		de->flags |= DE_UP;

	x = s->x - dd->wl;
	if (de->prev && de->prev->s == s
	 && ((de->flags ^ de->prev->flags) & DE_UP) == 0) {
		dd2 = &deco_def_tb[de->prev->t];
		if (dd2->func >= 6) {	/* if dynamic mark */
			x2 = de->prev->x + de->prev->v + 4;
			if (x2 > x)
				x = x2;
		}
#if 0
//fixme:test volume shift
// does not work with
//	cE!p!E !fff!Ceg|
	} else if (!up && s->stem < 0 && s->ymn < 10) {
		float y;

		x2 = x - (STEM_XOFF + dd->wr + 4);
		y = y_get(s->staff, up, x2, de->v);
		if (y > s->ymn) {
			x = x2;
		} else {
			x2 -= 3;
			y = y_get(s->staff, up, x2, de->v);
			if (y > s->ymn)
				x = x2;
		}
#endif
	}

	de->x = x;
	de->y = y_get(s->staff, up, x, de->v);
	if (!up)
		de->y -= dd->h;

	str = dd->name;
	if (dd->strx != 0 && dd->strx != 255)
		str = set_str(de, str_tb[dd->strx]);
	de->str = str;
	/* (y_set is done later in draw_deco_staff) */
}

/* special case for slide and tremolo */
static void d_slide(struct deco_elt *de)
{
	struct SYMBOL *s;
	int m, yc;
	float xc, dx;

	s = de->s;
	yc = s->pits[0];
	xc = 5;
	for (m = 0; m <= s->nhd; m++) {
		if (s->as.u.note.accs[m]) {
			dx = 4 + s->shac[m];
		} else {
			dx = 5 - s->shhd[m];
			switch (s->head) {
			case H_SQUARE:
			case H_OVAL:
				dx += 2.5;
				break;
			}
		}
		if (s->pits[m] <= yc + 3 && dx > xc)
			xc = dx;
	}
	de->x = s->x - xc;
	de->y = (float) (3 * (yc - 18));
}

/* special case for long trill */
static void d_trill(struct deco_elt *de)
{
	struct SYMBOL *s, *s2;
	struct deco_def_s *dd;
	int staff, up;
	float x, y, w;

	if (de->flags & DE_LDST)
		return;
	s2 = de->s;

	if (de->start) {		/* deco start */
		s = de->start->s;
		x = s->x;
		if (s->as.type == ABC_T_NOTE
		 && s->as.u.note.dc.n > 1)
			x += 10;
	} else {			/* end without start */
		s = first_note;
		x = s->x - s->wl - 4;
	}
	de->staff = staff = s2->staff;

	up = s2->multi >= 0;
	if (de->defl & DEF_NOEN) {	/* if no decoration end */
		w = de->x - x;
		if (w < 20) {
			x = de->x - 20 - 3;
			w = 20;
		}
	} else {
		w = s2->x - x - 6;
		if (s2->as.type == ABC_T_NOTE)
			w -= 6;
		if (w < 20) {
			x -= (20 - w) * 0.5;
			if (!de->start)
				x -= (20 - w) * 0.5;
			w = 20;
		}
	}

	dd = &deco_def_tb[de->t];
	y = y_get(staff, up, x, w);
	if (up) {
		float stafft;

		stafft = staff_tb[s->staff].topbar + 2;
		if (y < stafft)
			y = stafft;
	} else {
		float staffb;

		y -= dd->h;
		staffb = staff_tb[s->staff].botbar - 2;
		if (y > staffb)
			y = staffb;
	}
	de->flags &= ~DE_LDEN;
	de->flags |= DE_VAL;
	de->v = w;
	de->x = x;
	de->y = y;
	if (up)
		y += dd->h;
	y_set(staff, up, x, w, y);
	if (up)
		s->ymx = s2->ymx = y;
	else
		s->ymn = s2->ymn = y;
}

/* above (or below) the staff */
static void d_upstaff(struct deco_elt *de)
{
	struct SYMBOL *s;
	struct deco_def_s *dd;
	float x, yc, stafft, staffb, w;
	int inv;

	s = de->s;
	dd = &deco_def_tb[de->t];
	inv = 0;
	x = s->x + s->shhd[s->stem >= 0 ? 0 : s->nhd];
	w = dd->wl + dd->wr;
	stafft = staff_tb[s->staff].topbar + 2;
	staffb = staff_tb[s->staff].botbar - 2;

	switch (s->posit.orn) {
	case SL_ABOVE:
		de->flags &= ~DE_BELOW;
		break;
	case SL_BELOW:
		de->flags |= DE_BELOW;
		break;
	}

	if (strcmp(dd->name, ">") == 0
	 || strcmp(dd->name, "accent") == 0
	 || strcmp(dd->name, "emphasis") == 0
	 || strcmp(dd->name, "roll") == 0) {
		if (s->multi < 0
		 || (s->multi == 0 && s->stem > 0)) {
			yc = y_get(s->staff, 0, s->x - dd->wl, w);
			if (yc > staffb)
				yc = staffb;
			yc -= dd->h;
			y_set(s->staff, 0, s->x, 0, yc);
			inv = 1;
			s->ymn = yc;
		} else {
			yc = y_get(s->staff, 1, s->x, 0);
			if (yc < stafft)
				yc = stafft;
			y_set(s->staff, 1, s->x - dd->wl, w, yc + dd->h);
			s->ymx = yc + dd->h;
		}
	} else if (strcmp(dd->name, "breath") == 0
		|| strcmp(dd->name, "longphrase") == 0
		|| strcmp(dd->name, "mediumphrase") == 0
		|| strcmp(dd->name, "shortphrase") == 0) {
		yc = stafft + 1;
		for (s = s->ts_next; s; s = s->ts_next)
			if (s->shrink != 0)
				break;
		if (s)
			x += (s->x - x) * 0.4;
		else
			x += (realwidth - x) * 0.4;
	} else {
		if (strcmp(dd->name, "invertedturn") == 0
		 || strcmp(dd->name, "invertedturnx") == 0)
			inv = 1;
		if (s->multi >= 0
		 && strcmp(dd->name, "invertedfermata") != 0
		 && !(de->flags & DE_BELOW)) {
			yc = y_get(s->staff, 1, s->x - dd->wl, w);
			if (yc < stafft)
				yc = stafft;
			y_set(s->staff, 1, s->x - dd->wl, w, yc + dd->h);
			s->ymx = yc + dd->h;
		} else {
			yc = y_get(s->staff, 0, s->x - dd->wl, w);
			if (yc > staffb)
				yc = staffb;
			yc -= dd->h;
			y_set(s->staff, 0, s->x - dd->wl, w, yc);
			if (strcmp(dd->name, "fermata") == 0
			 || strcmp(dd->name, "invertedfermata") == 0)
				inv = 1;
			s->ymn = yc;
		}
	}
	if (inv) {
		yc += dd->h;
		de->flags |= DE_INV;
	}
	de->x = x;
	de->y = yc;

	if (dd->strx != 0)
		de->str = set_str(de,
				dd->strx == 255 ? dd->name : str_tb[dd->strx]);
}

/* -- add a decoration - from %%deco -- */
/* syntax:
 *	%%deco <name> <c_func> <ps_func> <h> <wl> <wr> [<str>]
 */
void deco_add(char *s)
{
	struct u_deco *d;
	int l;

	l = strlen(s);
	d = malloc(sizeof *user_deco - sizeof user_deco->text + l + 1);
	strcpy(d->text, s);
	d->next = user_deco;
	user_deco = d;
}

static int get_deco(char *name)
{
	struct deco_def_s *dd;
	int ideco;

	for (ideco = 1, dd = &deco_def_tb[1]; ideco < 128; ideco++, dd++) {
		if (!dd->name
		 || strcmp(dd->name, name) == 0)
			return ideco;
	}
	error(1, NULL, "Too many decorations");
	return ideco;
}

static unsigned char deco_build(char *name, char *text)
{
	struct deco_def_s *dd;
	int c_func, ideco, h, o, wl, wr, n;
	unsigned l, ps_x, strx;
	char name2[32];
	char ps_func[16];

	/* extract the arguments */
	if (sscanf(text, "%15s %d %15s %d %d %d%n",
			name2, &c_func, ps_func, &h, &wl, &wr, &n) != 6) {
		error(1, NULL, "Invalid deco %s", text);
		return 128;
	}
	if ((unsigned) c_func >= sizeof func_tb / sizeof func_tb[0]
	 && (c_func < 32 || c_func > 40)) {
		error(1, NULL, "%%%%deco: bad C function index (%s)", text);
		return 128;
	}
	if (h < 0 || wl < 0 || wr < 0) {
		error(1, NULL, "%%%%deco: cannot have a negative value (%s)", text);
		return 128;
	}
	if (h > 50 || wl > 80 || wr > 80) {
		error(1, NULL, "%%%%deco: abnormal h/wl/wr value (%s)", text);
		return 128;
	}
	text += n;
	while (isspace((unsigned char) *text))
		text++;

	/* search the decoration */
	ideco = get_deco(name);
	if (ideco == 128)
		return ideco;
	dd = &deco_def_tb[ideco];

	/* search the postscript function */
	for (ps_x = 0; ps_x < sizeof ps_func_tb / sizeof ps_func_tb[0]; ps_x++) {
		if (ps_func_tb[ps_x] == 0
		 || strcmp(ps_func_tb[ps_x], ps_func) == 0)
			break;
	}
	if (ps_x == sizeof ps_func_tb / sizeof ps_func_tb[0]) {
		error(1, NULL, "Too many postscript functions");
		return 128;
	}

	/* have an index for the string */
	if (*text == '\0') {
		strx = 0;
	} else if (strcmp(text, name) == 0) {
		strx = 255;
	} else {
		for (strx = 1;
		     strx < sizeof str_tb / sizeof str_tb[0];
		     strx++) {
			if (str_tb[strx] == 0) {
				if (*text == '"') {
					text++;
					l = strlen(text);
					str_tb[strx] = malloc(l);
					memcpy(str_tb[strx], text, l - 1);
					str_tb[strx][l - 1] = '\0';
				} else {
					str_tb[strx] = strdup(text);
				}
				break;
			}
			if (strcmp(str_tb[strx], text) == 0)
				break;
		}
		if (strx == sizeof str_tb / sizeof str_tb[0]) {
			error(1, NULL, "Too many decoration strings");
			return 128;
		}
	}

	/* set the values */
	if (!dd->name)
		dd->name = name;		/* new decoration */
	dd->func = c_func;
	if (!ps_func_tb[ps_x]) {
		if (ps_func[0] == '-' && ps_func[1] == '\0')
			ps_x = -1;
		else
			ps_func_tb[ps_x] = strdup(ps_func);
	}
	dd->ps_func = ps_x;
	dd->h = h;
	dd->wl = wl;
	dd->wr = wr;
 	dd->strx = strx;

	/* link the start and end of long decorations */
	l = strlen(name);
	if (l == 0)
		return ideco;
	l--;
	if (name[l] == '('
	 || (name[l] == ')' && !strchr(name, '('))) {
		struct deco_def_s *ddo;

		if (name[l] == '(')
			dd->flags = DE_LDST;
		else
			dd->flags = DE_LDEN;
		for (o = 1, ddo = &deco_def_tb[1]; o < 128; o++, ddo++) {
			if (!ddo->name)
				break;
			if (strlen(ddo->name) == l + 1
			 && strncmp(ddo->name, name, l) == 0) {
				if (name[l] == '('
				 && ddo->name[l] == ')') {
					dd->ld_end = o;
					break;
				}
				if (name[l] == ')'
				 && ddo->name[l] == '(') {
					ddo->ld_end = ideco;
					break;
				}
			}
		}
	}
	return ideco;
}

/* -- set the duration of the notes under a feathered beam -- */
static void set_feathered_beam(struct SYMBOL *s1,
				int accel)
{
	struct SYMBOL *s, *s2;
	int n, t, tt, d, b, i;
	float a;

	/* search the end of the beam */
	d = s1->dur;
	s2 = NULL;
	n = 1;
	for (s = (struct SYMBOL *) s1->as.next;
	     s;
	     s = (struct SYMBOL *) s->as.next) {
		if (s->dur != d
		 || (s->as.flags & ABC_F_SPACE))
			break;
		s2 = s;
		n++;
	}
	if (!s2)
		return;
	b = d / 2;			/* smallest note duration */
	a = (float) d / (n - 1);		/* delta duration */
	tt = d * n;
	t = 0;
	if (accel) {				/* !beam-accel! */
		for (s = s1, i = n - 1;
		     s != s2;
		     s = (struct SYMBOL *) s->as.next, i--) {
			d = (int) lroundf(a * i) + b;
			s->dur = d;
			t += d;
		}
	} else {				/* !beam-rall! */
		for (s = s1, i = 0;
		     s != s2;
		     s = (struct SYMBOL *) s->as.next, i++) {
			d = (int) lroundf(a * i) + b;
			s->dur = d;
			t += d;
		}
	}
	s2->dur = tt - t;
}

/* -- define a user decoration -- */
static unsigned char user_deco_define(char *name)
{
	struct u_deco *d;
	int l;

	l = strlen(name);
	for (d = user_deco; d; d = d->next) {
		if (strncmp(d->text, name, l) == 0
		 && d->text[l] == ' ')
			return deco_build(name, d->text);
	}
	return 128;
}

/* -- define a standard decoration -- */
static unsigned char deco_define(char *name)
{
	unsigned char ideco;
	int l;

	l = strlen(name);
	for (ideco = 0; ; ideco++) {
		if (!std_deco_tb[ideco])
			return 128;
		if (strncmp(std_deco_tb[ideco], name, l) == 0
		 && std_deco_tb[ideco][l] == ' ')
			break;
	}
	return deco_build(name, std_deco_tb[ideco]);
}

/* -- convert the external deco number to the internal one -- */
static unsigned char deco_intern(unsigned char ideco,
				struct SYMBOL *s)
{
	char *name;

	if (ideco < 128) {
		name = deco[ideco];
		if (!name) {
			error(1, s, "Bad character '%c'", ideco);
			return 0;
		}
	} else {
		name = parse.deco_tb[ideco - 128];
	}
	for (ideco = 1; ideco < 128; ideco++) {
		if (!deco_def_tb[ideco].name) {
			ideco = user_deco_define(name);	/* try a user decoration */
			if (ideco == 128)		/* try a standard decoration */
				ideco = deco_define(name);
			break;
		}
		if (strcmp(deco_def_tb[ideco].name, name) == 0)
			break;
	}
	if (ideco == 128) {
		error(1, s, "Decoration !%s! not treated", name);
		ideco = 0;
	}
	return ideco;
}

/* -- convert the decorations -- */
void deco_cnv(struct decos *dc,
		struct SYMBOL *s,
		struct SYMBOL *prev)
{
	int i, j;
	struct deco_def_s *dd;
	unsigned char ideco;
	static char must_note_fmt[] = "Deco !%s! must be on a note";

	for (i = dc->n; --i >= 0; ) {
		if ((ideco = dc->tm[i].t) == 0)
			continue;
		ideco = deco_intern(ideco, s);
		dc->tm[i].t = ideco;
		if (ideco == 0)
			continue;

		/* special decorations */
		dd = &deco_def_tb[ideco];
		switch (dd->func) {
		default:
			continue;
		case 32:		/* 32 = invisible */
			s->as.flags |= ABC_F_INVIS;
			break;
		case 33:		/* 33 = beamon */
			s->sflags |= S_BEAM_ON;
			break;
		case 34:		/* 34 = trem1..trem4 */
			if (s->as.type != ABC_T_NOTE
			 || !prev
			 || prev->as.type != ABC_T_NOTE) {
				error(1, s,
					"!%s! must be on the last of a couple of notes",
					dd->name);
				break;
			}
			s->sflags |= (S_TREM2 | S_BEAM_END);
			s->sflags &= ~S_BEAM_ST;
			prev->sflags |= (S_TREM2 | S_BEAM_ST);
			prev->sflags &= ~S_BEAM_END;
			s->u = prev->u = dd->name[4] - '0';
			for (j = 0; j <= s->nhd; j++)
				s->as.u.note.lens[j] *= 2;
			for (j = 0; j <= prev->nhd; j++)
				prev->as.u.note.lens[j] *= 2;
			break;
		case 35:		/* 35 = xstem */
			if (s->as.type != ABC_T_NOTE) {
				error(1, s, must_note_fmt, dd->name);
				break;
			}
			s->sflags |= S_XSTEM;
			break;
		case 36:		/* 36 = beambr1 / beambr2 */
			if (s->as.type != ABC_T_NOTE) {
				error(1, s, must_note_fmt, dd->name);
				break;
			}
			s->sflags |= dd->name[6] == '1' ?
					S_BEAM_BR1 : S_BEAM_BR2;
			break;
		case 37:		/* 37 = rbstop */
			s->sflags |= S_RBSTOP;
			break;
		case 38:		/* 38 = /, // and /// = tremolo */
			if (s->as.type != ABC_T_NOTE) {
				error(1, s, must_note_fmt, dd->name);
				break;
			}
			s->sflags |= S_TREM1;
			s->u = strlen(dd->name);	/* 1, 2 or 3 */
			break;
		case 39:		/* 39 = beam-accel/beam-rall */
			if (s->as.type != ABC_T_NOTE) {
				error(1, s, must_note_fmt, dd->name);
				break;
			}
			s->sflags |= S_FEATHERED_BEAM;
			set_feathered_beam(s, dd->name[5] == 'a');
			break;
		case 40:		/* 40 = stemless */
			if (s->as.type != ABC_T_NOTE) {
				error(1, s, must_note_fmt, dd->name);
				break;
			}
			s->as.flags |= ABC_F_STEMLESS;
			break;
		}
		dc->tm[i].t = 0;	/* already treated */
	}
}

/* -- update the x position of a decoration -- */
void deco_update(struct SYMBOL *s, float dx)
{
	struct deco_elt *de;

	for (de = deco_head; de; de = de->next) {
		if (de->s == s) {
			while (de && de->s == s) {
				de->x += dx;
				de = de->next;
			}
			break;
		}
	}
}

/* -- adjust the symbol width -- */
float deco_width(struct SYMBOL *s)
{
	struct decos *dc;
	int i;
	float wl;

	wl = 0;
	if (s->type == BAR)
		dc = &s->as.u.bar.dc;
	else
		dc = &s->as.u.note.dc;
	for (i = dc->n; --i >= 0; ) {
		struct deco_def_s *dd;

		dd =  &deco_def_tb[dc->tm[i].t];
		switch (dd->func) {
		case 1:			/* slide */
			if (wl < 7)
				wl = 7;
			break;
		case 2:			/* arpeggio */
			if (wl < 14)
				wl = 14;
			break;
		}
	}
	if (wl != 0 && s->prev && s->prev->type == BAR)
		wl -= 3;
	return wl;
}

/* -- draw the decorations -- */
/* (the staves are defined) */
void draw_all_deco(void)
{
	struct deco_elt *de;
	struct deco_def_s *dd;
	int f, staff;
	float x, y, y2, ym;
	float ymid[MAXSTAFF];

	if (!cfmt.dynalign) {
		staff = nstaff;
		y = staff_tb[staff].y;
		while (--staff >= 0) {
			y2 = staff_tb[staff].y;
			ymid[staff] = (y + 24 + y2) * 0.5;
			y = y2;
		}
	}

	for (de = deco_head; de; de = de->next) {
		dd = &deco_def_tb[de->t];
		if ((dd->flags & DE_LDST) && dd->ld_end != 0)
			continue;		// start of full long decoration
		if ((f = dd->ps_func) < 0)
			continue;		// old behaviour
		staff = de->staff;
		y = de->y + staff_tb[staff].y;

		/* center the dynamic marks between two staves */
/*fixme: KO when deco on other voice and same direction*/
		if (dd->func >= 6 && !cfmt.dynalign
		 && (((de->flags & DE_UP) && staff > 0)
		  || (!(de->flags & DE_UP) && staff < nstaff))) {
			if (de->flags & DE_UP)
				ym = ymid[--staff];
			else
				ym = ymid[staff++];
			ym -= dd->h * 0.5;
			if (((de->flags & DE_UP) && y < ym)
			 || (!(de->flags & DE_UP) && y > ym)) {
//				struct SYMBOL *s;
//
//				s = de->s;
//				if (s->staff > staff) {
//					while (s->staff != staff)
//						s = s->ts_prev;
//				} else if (s->staff < staff) {
//					while (s->staff != staff)
//						s = s->ts_next;
//				}
				y2 = y_get(staff, !(de->flags & DE_UP),
							de->x, de->v)
					+ staff_tb[staff].y;
				if (de->flags & DE_UP)
					y2 -= dd->h;
				if (((de->flags & DE_UP) && y2 > ym)
				 || (!(de->flags & DE_UP) && y2 < ym)) {
					y = ym;
					y_set(staff, de->flags & DE_UP,
							de->x, de->v,
						  ((de->flags & DE_UP) ? y + dd->h : y)
						- staff_tb[staff].y);
				}
			}
		}

		set_v_color(de->s->voice);
		set_scale(de->s);
		set_defl(de->defl);
/*fixme: scaled or not?*/
		if (de->flags & DE_VAL)
			putf(de->v);
		x = de->x;
		if (de->str) {
			char *p, *q;

			y += dd->h * 0.2;		// font descent
			p = de->str;
			if (dd->strx != 0 && dd->strx != 255
			 && str_tb[dd->strx][0] == '@') { // annotation like
				str_font(ANNOTATIONFONT);
				outft = -1;		// force font selection
				putxy(x, y + de->dy);
				a2b("M");
				put_str(p, A_LEFT);
				continue;
			}
			a2b("(");
			q = p;
			while (*p != '\0') {
				if (*p == '(' || *p == ')') {
					if (p != q)
						a2b("%.*s", (int) (p - q), q);
					a2b("\\");
					q = p;
				}
				p++;
			}
			if (p != q)
				a2b("%.*s", (int) (p - q), q);
			a2b(")");
		}
		putxy(x, y);
		if (de->flags & DE_LDEN) {
			if (de->start) {
				x = de->start->x;
				y = de->start->y + staff_tb[de->start->staff].y;
			} else {
				x = first_note->x - first_note->wl - 4;
			}
			if (x > de->x - 20)
				x = de->x - 20;
			putxy(x, y);
		}
		if (de->flags & DE_GRACE) {
			if (de->flags & DE_INV)
				a2b("gsave T 0.7 -0.7 scale 0 0 %s grestore\n",
						ps_func_tb[f]);
			else
				a2b("gsave T 0.7 dup scale 0 0 %s grestore\n",
						ps_func_tb[f]);
		} else {
			if (de->flags & DE_INV)
				a2b("gsave 1 -1 scale neg %s grestore\n",
						ps_func_tb[f]);
			else
				a2b("%s\n", ps_func_tb[f]);
		}
	}
	set_sscale(-1);			/* restore the scale */
	set_v_color(-1);
}

/* -- draw a decoration relative to a note head -- */
/* return 1 if the decoration replaces the head */
int draw_deco_head(int ideco, float x, float y, int stem)
{
	struct deco_def_s *dd;
	char *str;

	if (ideco == 0)
		return 0;
	dd = &deco_def_tb[ideco];
	if (dd->ps_func < 0)
		return 0;
	if (cfmt.setdefl)
		set_defl(stem >= 0 ? DEF_STEMUP : 0);
	switch (dd->func) {
	case 2:
	case 5:
	case 7:
		a2b("0 ");
		break;
	case 3:
	case 4:
		if (dd->strx == 0)
			break;
		/* fall thru */
	case 6:
		str = dd->name;
		if (dd->strx != 0 && dd->strx != 255)
			str = str_tb[dd->strx];
// no de!
//			str = set_str(de, str_tb[dd->strx]);
		a2b("(%s)", str);
		break;
	}
// no de!
	putxy(x, y);
//	putxy(x, y + de->dy);
	a2b("%s ", ps_func_tb[dd->ps_func]);
	return strncmp(dd->name, "head-", 5) == 0;
}

/* -- create the deco elements, and treat the near ones -- */
static void deco_create(struct SYMBOL *s,
			struct decos *dc)
{
	int k, posit;
	unsigned char ideco;
	struct deco_def_s *dd;
	struct deco_elt *de;
#if 1
/*fixme:pb with decorations above the staff*/
	for (k = 0; k < dc->n; k++) {
		if (dc->tm[k].m != 255)		/* skip the head decorations */
			continue;
		if ((ideco = dc->tm[k].t) == 0)
			continue;
		dd = &deco_def_tb[ideco];
#else
	int i, j;
	struct deco_def_s *d_tb[MAXDC];

	/* the decorations above the staff must be treated in reverse order */ 
	memset(&d_tb, 0, sizeof d_tb);
	i = 0;
	j = dc->n;
	for (k = 0; k < dc->n; k++) {
		if (dc->tm[k].m != 255)		/* skip the head decorations */
			continue;
		if ((ideco = dc->tm[k].t) == 0)
			continue;
		dd = &deco_def_tb[ideco];
		if (dd->func < 3) {		/* if near the note */
			if (s->multi > 0
			 || (s->multi == 0 && s->stem < 0)) {
				d_tb[--j] = dd;
				continue;
			}
		} else if (dd->func == 3	/* if tied to note (not below) */
			|| dd->func == 5) {
			if (s->multi >= 0) {
				d_tb[--j] = dd;
				continue;
			}
		}
		d_tb[i++] = dd;
	}

	for (k = 0; k < dc->n; k++) {
		if ((dd = d_tb[k]) == 0)
			continue;
#endif
		/* check if hidden */
		switch (dd->func) {
		default:
			posit = 0;
			break;
		case 3:				/* d_upstaff */
		case 4:
//fixme:trill does not work yet
		case 5:				/* trill */
			posit = s->posit.orn;
			break;
		case 6:				/* d_pf */
			posit = s->posit.vol;
			break;
		case 7:				/* d_cresc */
			posit = s->posit.dyn;
			break;
		}
		if (posit == SL_HIDDEN) {
			dc->tm[k].t = 0;
			continue;
		}

		/* memorize the decorations, but not the head ones */
		if (strncmp(dd->name, "head-", 5) == 0) {
			if (s->type != NOTEREST) {
				error(1, s, "Cannot have !%s! on a bar",
					dd->name);
				break;
			}
			continue;
		}
		de = (struct deco_elt *) getarena(sizeof *de);
		memset(de, 0, sizeof *de);
		de->prev = deco_tail;
		if (!deco_tail)
			deco_head = de;
		else
			deco_tail->next = de;
		deco_tail = de;
		de->s = s;
		de->t = dd - deco_def_tb;
		de->staff = s->staff;
		if (s->as.flags & ABC_F_GRACE)
			de->flags = DE_GRACE;
		if (dd->flags & DE_LDST) {
			de->flags |= DE_LDST;
		} else if (dd->flags & DE_LDEN) {
			de->flags |= DE_LDEN;
			de->defl = DEF_NOST;
		}
		if (cfmt.setdefl && s->stem >= 0)
			de->defl |= DEF_STEMUP;

		if (dd->func >= 3)	/* if not near the note */
			continue;
		if (s->as.type != ABC_T_NOTE) {
			error(1, s,
				"Cannot have !%s! on a rest or a bar",
				dd->name);
			continue;
		}
		func_tb[dd->func](de);
	}
}

/* -- create the decorations and treat the ones near the notes -- */
/* (the staves are not yet defined) */
/* this function must be called first as it builds the deco element table */
void draw_deco_near(void)
{
	struct SYMBOL *s, *g;
	struct decos *dc;
	struct SYMBOL *first;

	deco_head = deco_tail = NULL;
	first = NULL;
	for (s = tsfirst; s; s = s->ts_next) {
		switch (s->type) {
		case BAR:
		case MREST:
			if (s->as.u.bar.dc.n == 0)
				continue;
			dc = &s->as.u.bar.dc;
			break;
		case NOTEREST:
		case SPACE:
			if (!first)
				first = s;
			if (s->as.u.note.dc.n == 0)
				continue;
			dc = &s->as.u.note.dc;
			break;
		case GRACE:
			for (g = s->extra; g; g = g->next) {
				if (g->as.type != ABC_T_NOTE
				 || g->as.u.note.dc.n == 0)
					continue;
				dc = &g->as.u.note.dc;
				deco_create(g, dc);
			}
			/* fall thru */
		default:
			continue;
		}
		deco_create(s, dc);
	}
	first_note = first;
}

/* -- draw the decorations tied to a note -- */
/* (the staves are not yet defined) */
void draw_deco_note(void)
{
	struct deco_elt *de, *de2;
	struct deco_def_s *dd;
	int f, t, staff, voice;

	for (de = deco_head; de; de = de->next) {
		t = de->t;
		dd = &deco_def_tb[t];

		// link the long decorations
		if (de->flags & DE_LDST) {	/* start of long decoration */
			t = dd->ld_end;
			if (t == 0) {		// if long deco has no end
				int l;		// create one
				char *name;

				l = strlen(dd->name);
				name = getarena(l + 1);
				strcpy(name, dd->name);
				name[l - 1] = ')';
				t = get_deco(name);
				if (t != 128) {
					struct deco_def_s *dd2;

					dd2 = &deco_def_tb[t];
					dd2->name = name;
					dd2->func = dd->func;
					dd2->ps_func = dd->ps_func;
					dd2->h = dd->h;
					dd->ld_end = t;
				} else {
					t = 0;
				}
			}
			voice = de->s->voice;	/* search in the voice */
			for (de2 = de->next; de2; de2 = de2->next)
				if (de2->t == t && de2->s->voice == voice)
					break;
			if (!de2) {		/* search in the staff */
				staff = de->s->staff;
				for (de2 = de->next; de2; de2 = de2->next)
					if (de2->t == t && de2->s->staff == staff)
						break;
			}
			if (!de2) {		/* no end, insert one */
				de2 = (struct deco_elt *) getarena(sizeof *de2);
				memset(de2, 0, sizeof *de2);
				de2->prev = deco_tail;
				deco_tail->next = de2;
				deco_tail = de2;
				de2->s = de->s;
				de2->t = t;
				de2->defl = DEF_NOEN;
				de2->flags = DE_LDEN;
				de2->x = realwidth - 6;
				de2->y = de->s->y;
			}
			de2->start = de;
			de2->defl &= ~DEF_NOST;
		}
		f = dd->func;
		if (f < 3 || f >= 6)
			continue;	/* not tied to the note */
		if (f == 4)
			de->flags |= DE_BELOW;
		func_tb[f](de);
	}
}

/* -- draw the music elements tied to the staff -- */
/* (the staves are not yet defined) */
void draw_deco_staff(void)
{
	struct SYMBOL *s, *first_gchord;
	struct VOICE_S *p_voice;
	float x, y, w;
	struct deco_elt *de;
	struct {
		float ymin, ymax;
	} minmax[MAXSTAFF];

//	outft = -1;				/* force font output */

	/* search the vertical offset for the guitar chords */
	memset(minmax, 0, sizeof minmax);
	first_gchord = 0;
	for (s = tsfirst; s; s = s->ts_next) {
		struct gch *gch, *gch2;
		int ix;

		gch = s->gch;
		if (!gch)
			continue;
		if (!first_gchord)
			first_gchord = s;
		gch2 = NULL;
		for (ix = 0; ix < MAXGCH; ix++, gch++) {
			if (gch->type == '\0')
				break;
			if (gch->type != 'g')
				continue;
			gch2 = gch;	/* guitar chord closest to the staff */
			if (gch->y < 0)
				break;
		}
		if (gch2) {
			w = gch2->w;
			if (gch2->y >= 0) {
				y = y_get(s->staff, 1, s->x, w);
				if (y > minmax[s->staff].ymax)
					minmax[s->staff].ymax = y;
			} else {
				y = y_get(s->staff, 0, s->x, w);
				if (y < minmax[s->staff].ymin)
					minmax[s->staff].ymin = y;
			}
		}
	}

	/* draw the guitar chords if any */
	if (first_gchord) {
		int i;

		for (i = 0; i <= nstaff; i++) {
			int top, bot;

			bot = staff_tb[i].botbar;
			minmax[i].ymin -= 3;
			if (minmax[i].ymin > bot - 10)
				minmax[i].ymin = bot - 10;
			top = staff_tb[i].topbar;
			minmax[i].ymax += 3;
			if (minmax[i].ymax < top + 10)
				minmax[i].ymax = top + 10;
		}
		set_sscale(-1);		/* restore the scale parameters */
		for (s = first_gchord; s; s = s->ts_next) {
			if (!s->gch)
				continue;
			switch (s->type) {
			case NOTEREST:
			case SPACE:
			case MREST:
				break;
			case BAR:
				if (!s->as.u.bar.repeat_bar)
					break;
			default:
				continue;
			}
			draw_gchord(s, minmax[s->staff].ymin,
					minmax[s->staff].ymax);
		}
	}

	/* draw the repeat brackets */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		struct SYMBOL *s1, *s2, *first_repeat;
		float y2;
		int i, repnl;

		if (p_voice->second || !p_voice->sym)
			continue;

		/* search the max y offset and set the end of bracket */
		y = staff_tb[p_voice->staff].topbar + 6 + 20;
		first_repeat = 0;
		for (s = p_voice->sym->next; s; s = s->next) {
			if (s->type != BAR
			 || !s->as.u.bar.repeat_bar
			 || (s->sflags & S_NOREPBRA))
				continue;
/*fixme: line cut on repeat!*/
			if (!s->next)
				break;
			if (!first_repeat)
				first_repeat = s;
			s1 = s;

			/* a bracket may be 4 measures
			 * but only 2 measures when it has no start */
			i = s1->as.text ? 4 : 2;
			for (;;) {
				if (!s->next)
					break;
				s = s->next;
				if (s->sflags & S_RBSTOP)
					break;
				if (s->type != BAR)
					continue;
				if (((s->as.u.bar.type & 0xf0)	/* if complex bar */
				   && s->as.u.bar.type != (B_OBRA << 4) + B_CBRA)
				  || s->as.u.bar.type == B_CBRA
				  || s->as.u.bar.repeat_bar)
					break;
				if (--i <= 0) {

					/* have a shorter repeat bracket */
					s = s1;
					i = 2;
					for (;;) {
						s = s->next;
						if (s->type != BAR)
							continue;
						if (--i <= 0)
							break;
					}
					s->sflags |= S_RBSTOP;
					break;
				}
			}
			y2 = y_get(p_voice->staff, 1, s1->x, s->x - s1->x);
			if (y < y2)
				y = y2;

			/* have room for the repeat numbers */
			if (s1->gch) {
				w = s1->gch->w;
				y2 = y_get(p_voice->staff, 1, s1->x + 4, w);
				y2 += cfmt.font_tb[REPEATFONT].size + 2;
				if (y < y2)
					y = y2;
			}
			if (s->as.u.bar.repeat_bar)
				s = s->prev;
		}

		/* draw the repeat indications */
		s = first_repeat;
		if (!s)
			continue;
		set_sscale(p_voice->staff);
		set_font(REPEATFONT);
		repnl = 0;
		for ( ; s; s = s->next) {
			char *p;

			if (s->type != BAR
			 || !s->as.u.bar.repeat_bar
			 || (s->sflags & S_NOREPBRA))
				continue;
			s1 = s;
			for (;;) {
				if (!s->next)
					break;
				s = s->next;
				if (s->sflags & S_RBSTOP)
					break;
				if (s->type != BAR)
					continue;
				if (((s->as.u.bar.type & 0xf0)	/* if complex bar */
				  && s->as.u.bar.type != (B_OBRA << 4) + B_CBRA)
				 || s->as.u.bar.type == B_CBRA
				 || s->as.u.bar.repeat_bar)
					break;
			}
			s2 = s;
			if (s1 == s2)
				break;
			x = s1->x;
			if ((s1->as.u.bar.type & 0x07) == B_COL)
				x -= 4;
			i = 0;				/* no bracket end */
			if (s2->sflags & S_RBSTOP) {
				w = 8;			/* (w = left shift) */
			} else if (s2->type != BAR) {
				w = s2->x - realwidth + 4;
			} else if (((s2->as.u.bar.type & 0xf0)	/* if complex bar */
				 && s2->as.u.bar.type != (B_OBRA << 4) + B_CBRA)
				|| s2->as.u.bar.type == B_CBRA) {
				i = 2;			/* bracket start and stop */
/*fixme:%%staves: cursys moved?*/
				if (s->staff > 0
				 && !(cursys->staff[s->staff - 1].flags & STOP_BAR)) {
					w = s2->wl;
				} else if ((s2->as.u.bar.type & 0x0f) == B_COL) {
					w = 12;
				} else if (!(s2->sflags & S_RRBAR)
					|| s2->as.u.bar.type == B_CBRA) {
					w = 0;		/* explicit repeat end */

					/* if ']', don't display as thick bar */
					if (s2->as.u.bar.type == B_CBRA)
						s2->as.flags |= ABC_F_INVIS;
				} else {
					w = 8;
				}
			} else {
				w = 8;
			}
			w = s2->x - x - w;
			p = s1->as.text;
			if (!p) {
				i--;		/* no bracket start (1) or not drawn */
				p = "";
			}
			if (i == 0 && !s2->next	/* 2nd ending at end of line */
			 && !(s2->sflags & S_RBSTOP)) {
				if (p_voice->bar_start == 0)
					repnl = 1;	/* continue on next line */
			}
			if (i >= 0) {
				a2b("(%s)-%.1f %d ",
					p, cfmt.font_tb[REPEATFONT].size * 0.8 + 1, i);
				putx(w);
				putxy(x, y);
				a2b("y%d repbra\n", s1->staff);
				y_set(s1->staff, 1, x, w, y + 2);
			}
			if (s->as.u.bar.repeat_bar)
				s = s->prev;
		}
		if (repnl) {
			p_voice->bar_start = B_OBRA;
			p_voice->bar_repeat = 1;
		}
	}

	/* create the decorations tied to the staves */
	memset(minmax, 0, sizeof minmax);
	for (de = deco_head; de; de = de->next) {
		struct deco_def_s *dd;

		dd = &deco_def_tb[de->t];
		if (dd->func < 6)		/* if not tied to the staff */
			continue;
		func_tb[dd->func](de);
		if (dd->ps_func < 0)
			continue;
		if (cfmt.dynalign) {
			if (de->flags & DE_UP) {
				if (de->y > minmax[de->staff].ymax)
					minmax[de->staff].ymax = de->y;
			} else {
				if (de->y < minmax[de->staff].ymin)
					minmax[de->staff].ymin = de->y;
			}
		}
	}

	/* and, if wanted, set them at a same vertical offset */
	for (de = deco_head; de; de = de->next) {
		struct deco_def_s *dd;

		dd = &deco_def_tb[de->t];
		if (dd->ps_func < 0
		 || dd->func < 6)
			continue;
		if (cfmt.dynalign) {
			if (de->flags & DE_UP)
				y = minmax[de->staff].ymax;
			else
				y = minmax[de->staff].ymin;
			de->y = y;
		} else {
			y = de->y;
		}
		if (de->flags & DE_UP)
			y += dd->h;
		y_set(de->staff, de->flags & DE_UP, de->x, de->v, y);
	}
}

/* -- draw the guitar chords and annotations -- */
/* (the staves are not yet defined) */
static void draw_gchord(struct SYMBOL *s,
			float gchy_min, float gchy_max)
{
	struct gch *gch, *gch2;
	int action, ix, box;
	float x, y, w, h, y_above, y_below;
	float hbox, xboxl, yboxh, yboxl, expdx;

	/* adjust the vertical offset according to the guitar chords */
//fixme: w may be too small
	w = s->gch->w;
#if 1
	y_above = y_get(s->staff, 1, s->x - 2, w);
	y_below = y_get(s->staff, 0, s->x - 2, w);
#else
	y_above = y_get(s->staff, 1, s->x - 2, w) + 2;
	y_below = y_get(s->staff, 0, s->x - 2, w) - 2;
#endif
	gch2 = NULL;
	for (ix = 0, gch = s->gch; ix < MAXGCH; ix++, gch++) {
		if (gch->type == '\0')
			break;
		if (gch->type != 'g')
			continue;
		gch2 = gch;		/* guitar chord closest to the staff */
		if (gch->y < 0)
			break;
	}
	if (gch2) {
		if (gch2->y >= 0) {
			if (y_above < gchy_max)
				y_above = gchy_max;
		} else {
			if (y_below > gchy_min)
				y_below = gchy_min;
		}
	}

	str_font(s->gch->font);
	set_font(s->gch->font);			/* needed if scaled staff */
	set_sscale(s->staff);
//	action = A_GCHORD;
	xboxl = s->x;
	yboxh = -100;
	yboxl = 100;
	box = 0;
	expdx = 0;
	for (ix = 0, gch = s->gch; ix < MAXGCH; ix++, gch++) {
		if (gch->type == '\0')
			break;
		h = cfmt.font_tb[gch->font].size;
		str_font(gch->font);
		tex_str(s->as.text + gch->idx);
		w = gch->w;
		if (gch->type == 'g') {			/* guitar chord */
			if (!strchr(tex_buf, '\t')) {
				action = A_GCHORD;
			} else {
				struct SYMBOL *next;
				char *r;
				int n;

				/* some TAB: expand the guitar chord */
				x = realwidth;
				next = s->next;
				while (next) {
					switch (next->type) {
					default:
						next = next->next;
						continue;
					case NOTEREST:
					case BAR:
						x = next->x;
						break;
					}
					break;
				}
				n = 0;
				r = tex_buf;
				for (;;) {
					n++;
					r = strchr(r, '\t');
					if (!r)
						break;
					r++;
				}
				expdx = (x - s->x - w) / n;
				action = A_GCHEXP;
			}
		} else {
			action = A_ANNOT;
		}
		x = s->x + gch->x;
		switch (gch->type) {
		case '_':			/* below */
			y = gch->y + y_below;
			y_set(s->staff, 0, x, w, y - h * 0.2 - 2);
			break;
		case '^':			/* above */
			y = gch->y + y_above;
			y_set(s->staff, 1, x, w, y + h * 0.8 + 2);
			break;
		default:			/* guitar chord */
			hbox = gch->box ? 3 : 2;
			if (gch->y >= 0) {
				y = gch->y + y_above;
				y_set(s->staff, 1, x, w, y + h + hbox);
			} else {
				y = gch->y + y_below;
				y_set(s->staff, 0, x, w, y - hbox);
			}
			if (gch->box) {
				if (xboxl > x)
					xboxl = x;
				if (yboxl > y)
					yboxl = y;
				if (yboxh < y + h)
					yboxh = y + h;
				box++;
			}
			break;
		case '<':			/* left */
/*fixme: what symbol space?*/
			if (s->as.u.note.accs[0])
				x -= s->shac[0];
			y = s->yav + gch->y;
			break;
		case '>':			/* right */
			x += s->xmx;
			if (s->dots > 0)
				x += 1.5 + 3.5 * s->dots;
			y = s->yav + gch->y;
			break;
		case '@':			/* absolute */
			y = s->yav + gch->y;
			break;
		}
		putxy(x, y + h * 0.2);		/* (descent) */
		a2b("y%d M ", s->staff);
		if (action == A_GCHEXP)
			a2b("%.2f ", expdx);
		str_out(tex_buf, action);
		if (gch->type == 'g' && box > 0) {
			if (box == 1)
				a2b(" boxstart");
			else
				a2b(" boxmark");
		}
		a2b("\n");
	}

	/* draw the box around the guitar chords */
	if (box) {
		xboxl -= 2;
		putxy(xboxl, yboxl - 1);
		a2b("y%d %.1f boxdraw\n", s->staff, yboxh - yboxl + 3);
	}
}

/* -- draw the measure bar numbers -- */
void draw_measnb(void)
{
	struct SYMBOL *s;
	struct SYSTEM *sy;
	char *showm;
	int any_nb, staff, bar_num;
	float x, y, w, font_size;

	showm = cfmt.measurebox ? "showb" : "show";
	any_nb = 0;

	/* search the first staff */
	sy = cursys;
	for (staff = 0; staff <= nstaff; staff++) {
		if (!sy->staff[staff].empty)
			break;
	}
	if (staff > nstaff)
		return;				/* no visible staff */
//fixme: must use the scale, otherwise bad y offset (y0 empty)
	set_sscale(staff);

	/* leave the measure numbers as unscaled */
	font_size = cfmt.font_tb[MEASUREFONT].size;
	cfmt.font_tb[MEASUREFONT].size /= staff_tb[staff].staffscale;

	s = tsfirst;				/* clef */
	bar_num = nbar;
	if (bar_num > 1) {
		if (cfmt.measurenb == 0) {
			set_font(MEASUREFONT);
			any_nb = 1;
			x = 0;
			w = 20;
			y = y_get(staff, 1, x, w);
			if (y < staff_tb[staff].topbar + 14)
				y = staff_tb[staff].topbar + 14;
			a2b("0 ");
			puty(y);
			a2b("y%d M(%d)%s", staff, bar_num, showm);
			y_set(staff, 1, x, w, y + cfmt.font_tb[MEASUREFONT].size + 2);
		} else if (bar_num % cfmt.measurenb == 0) {
			for ( ; ; s = s->ts_next) {
				switch (s->type) {
				case TIMESIG:
				case CLEF:
				case KEYSIG:
				case FMTCHG:
				case STBRK:
					continue;
				}
				break;
			}
			if (s->prev->type != CLEF)
				s = s->prev;
			x = s->x - s->wl;
			set_font(MEASUREFONT);
			any_nb = 1;
			w = cwid('0') * cfmt.font_tb[MEASUREFONT].size;
			if (bar_num >= 10) {
				if (bar_num >= 100)
					w *= 3;
				else
					w *= 2;
			}
			if (cfmt.measurebox)
				w += 4;
			y = y_get(staff, 1, x, w);
			if (y < staff_tb[staff].topbar + 6)
				y = staff_tb[staff].topbar + 6;
			y += 2;
			putxy(x, y);
			a2b("y%d M(%d)%s", staff, bar_num, showm);
			y += cfmt.font_tb[MEASUREFONT].size;
			y_set(staff, 1, x, w, y);
			s->ymx = y;
		}
	}

	for ( ; s; s = s->ts_next) {
		if (s->sflags & S_NEW_SY) {
			sy = sy->next;
			for (staff = 0; staff < nstaff; staff++) {
				if (!sy->staff[staff].empty)
					break;
			}
			set_sscale(staff);
		}
		if (s->type != BAR || s->u <= 0)
			continue;
		bar_num = s->u;
		if (cfmt.measurenb == 0
		 || (bar_num % cfmt.measurenb) != 0
		 || !s->next)
			continue;
		if (!any_nb) {
			any_nb = 1;
			set_font(MEASUREFONT);
		}
		w = cwid('0') * cfmt.font_tb[MEASUREFONT].size;
		if (bar_num >= 10) {
			if (bar_num >= 100)
				w *= 3;
			else
				w *= 2;
		}
		if (cfmt.measurebox)
			w += 4;
		x = s->x - w * 0.4;
		y = y_get(staff, 1, x, w);
		if (y < staff_tb[staff].topbar + 6)
			y = staff_tb[staff].topbar + 6;
		if (s->next->as.type == ABC_T_NOTE) {
			if (s->next->stem > 0) {
				if (y < s->next->ys - cfmt.font_tb[MEASUREFONT].size)
					y = s->next->ys - cfmt.font_tb[MEASUREFONT].size;
			} else {
				if (y < s->next->y)
					y = s->next->y;
			}
		}
		y += 2;
		a2b(" ");
		putxy(x, y);
		a2b("y%d M(%d)%s", staff, bar_num, showm);
		y += cfmt.font_tb[MEASUREFONT].size;
		y_set(staff, 1, x, w, y);
		s->ymx = y;
	}
	if (any_nb)
		a2b("\n");
	nbar = bar_num;

	cfmt.font_tb[MEASUREFONT].size = font_size;
}

/* -- get the beat from a time signature -- */
static int get_beat(struct meter_s *m)
{
	int top, bot;

	if (m->meter[0].top[0] == 'C') {
		if (m->meter[0].top[0] == '|')
			return BASE_LEN / 2;
		return BASE_LEN / 4;
	}
	if (m->meter[0].bot[0] == '\0')
		return BASE_LEN / 4;
	sscanf(m->meter[0].top, "%d", &top);
	sscanf(m->meter[0].bot, "%d", &bot);
	if (bot >= 8 && top >= 6 && top % 3 == 0)
		return BASE_LEN * 3 / 8;
	return BASE_LEN / bot;
}

/* -- draw the note of the tempo -- */
static void draw_notempo(struct SYMBOL *s, int len, float sc)
{
	int head, dots, flags;
	float dx;

	a2b("gsave %.2f dup scale 8 3 RM currentpoint ", sc);
	identify_note(s, len, &head, &dots, &flags);
	switch (head) {
	case H_OVAL:
		a2b("HD");
		break;
	case H_EMPTY:
		a2b("Hd");
		break;
	default:
		a2b("hd");
		break;
	}
	dx = 4;
	if (dots) {
		float dotx;

		dotx = 8;
		if (flags > 0)
			dotx += 4;
		switch (head) {
		case H_SQUARE:
		case H_OVAL:
			dotx += 2;
			break;
		case H_EMPTY:
			dotx += 1;
			break;
		}
		while (--dots >= 0) {
			a2b(" %.1f 0 dt", dotx);
			dx = dotx;
			dotx += 3.5;
		}
	}
	if (len < SEMIBREVE) {
		if (flags <= 0) {
			a2b(" %d su", STEM);
		} else {
			a2b(" %d %d sfu", flags, STEM);
			if (dx < 6)
				dx = 6;
		}
	}
	a2b(" grestore %.1f 0 RM\n", (dx + 15) * sc);
}

/* -- return the tempo width -- */
float tempo_width(struct SYMBOL *s)
{
	unsigned i;
	float w;

	w = 0;
	if (s->as.u.tempo.str1)
		w += tex_str(s->as.u.tempo.str1);
	if (s->as.u.tempo.value != 0) {
		i = 1;
		while (i < sizeof s->as.u.tempo.length
				/ sizeof s->as.u.tempo.length[0]
		       && s->as.u.tempo.length[i] > 0) {
			w += 10;
			i++;
		}
		w += 6 + cwid(' ') * cfmt.font_tb[TEMPOFONT].size * 6
			+ 10 + 10;
	}
	if (s->as.u.tempo.str2)
		w += tex_str(s->as.u.tempo.str2);
	return w;
}

/* - output a tempo --*/
void write_tempo(struct SYMBOL *s,
		 int beat,
		 float sc)
{
	int top, bot;
	unsigned j;

	if (s->as.u.tempo.str1)
		put_str(s->as.u.tempo.str1, A_LEFT);
	if (s->as.u.tempo.value != 0) {
		sc *= 0.7 * cfmt.font_tb[TEMPOFONT].size / 15.0;
						/*fixme: 15.0 = initial tempofont*/
		if (s->as.u.tempo.length[0] == 0) {
			if (beat == 0)
				beat = get_beat(&voice_tb[cursys->top_voice].meter);
			s->as.u.tempo.length[0] = beat;
		}
		for (j = 0;
		     j < sizeof s->as.u.tempo.length
				/ sizeof s->as.u.tempo.length[0]
			&& s->as.u.tempo.length[j] > 0;
		     j++) {
			draw_notempo(s, s->as.u.tempo.length[j], sc);
		}
		put_str("= ", A_LEFT);
		if (sscanf(s->as.u.tempo.value, "%d/%d", &top, &bot) == 2
		 && bot > 0)
			draw_notempo(s, top * BASE_LEN / bot, sc);
		else
			put_str(s->as.u.tempo.value, A_LEFT);
	}
	if (s->as.u.tempo.str2)
		put_str(s->as.u.tempo.str2, A_LEFT);
}

/* -- draw the parts and the tempo information -- */
/* (the staves are being defined) */
float draw_partempo(int staff, float top)
{
	struct SYMBOL *s, *g;
	int beat, dosh, shift;
	int some_part, some_tempo;
	float h, ht, w, x, y, ymin, dy;

	/* put the tempo indication at top */
	dy = 0;
	ht = 0;
	some_part = some_tempo = 0;

	/* get the minimal y offset */
	ymin = staff_tb[staff].topbar + 12;
	dosh = 0;
	shift = 1;
	x = 0;
	for (s = tsfirst; s; s = s->ts_next) {
		g = s->extra;
		if (!g)
			continue;
		for ( ; g; g = g->next)
			if (g->type == TEMPO)
				break;
		if (!g)
			continue;
		if (!some_tempo) {
			some_tempo = 1;
			str_font(TEMPOFONT);
		}
		w = tempo_width(g);
		y = y_get(staff, 1, s->x - 5, w) + 2;
		if (y > ymin)
			ymin = y;
		if (x >= s->x - 5 && !(dosh & (shift >> 1)))
			dosh |= shift;
		shift <<= 1;
		x = s->x - 5 + w;
	}
	if (some_tempo) {
		ht = cfmt.font_tb[TEMPOFONT].size + 2 + 2;
		y = 2 - ht;
		h = y - ht;
		if (dosh != 0)
			ht *= 2;
		if (top < ymin + ht)
			dy = ymin + ht - top;

		/* draw the tempo indications */
		str_font(TEMPOFONT);
		beat = 0;
		for (s = tsfirst; s; s = s->ts_next) {
			if (!(s->sflags & S_SEQST))
				continue;
			if (s->type == TIMESIG)
				beat = get_beat(&s->as.u.meter);
			g = s->extra;
//			if (!g)
//				continue;
			for ( ; g; g = g->next)
				if (g->type == TEMPO)
					break;
			if (!g)
				continue;

			/*fixme: cf left shift (-5)*/
			a2b("%.1f %.1f M ", s->x - 5,
					(dosh & 1) ? h : y);
			dosh >>= 1;
			write_tempo(g, beat, 1);
		}
	}

	/* then, put the parts */
/*fixme: should reduce if parts don't overlap tempo...*/
	ymin = staff_tb[staff].topbar + 14;
	for (s = tsfirst; s; s = s->ts_next) {
		g = s->extra;
		if (!g)
			continue;
		for (; g; g = g->next)
			if (g->type == PART)
				break;
		if (!g)
			continue;
		if (!some_part) {
			some_part = 1;
			str_font(PARTSFONT);
		}
		w = tex_str(&g->as.text[2]);
		y = y_get(staff, 1, s->x - 10, w + 15) + 5;
		if (ymin < y)
			ymin = y;
	}
	if (!some_part)
		goto out;

	h = cfmt.font_tb[PARTSFONT].size + 2 + 2;
						/* + cfmt.partsspace; ?? */
	if (top < ymin + h + ht)
		dy = ymin + h + ht - top;

	set_font(PARTSFONT);
	for (s = tsfirst; s; s = s->ts_next) {
		g = s->extra;
		if (!g)
			continue;
		for (; g; g = g->next)
			if (g->type == PART)
				break;
		if (!g)
			continue;
		w = tex_str(&g->as.text[2]);
		a2b("%.1f %.1f M", s->x - 10, 2 - ht - h);
		str_out(tex_buf, A_LEFT);
		if (cfmt.partsbox)
			a2b(" %.1f %.1f %.1f %.1f box",
				s->x - 10 - 2, 2 - ht - h - 4,
				w + 4, h);
		a2b("\n");
	}
out:
	return dy * staff_tb[staff].staffscale;
}

/* -- initialize the default decorations -- */
void init_deco(void)
{
	memset(&deco, 0, sizeof deco);

	/* standard */
	deco['.'] = "dot";
#ifdef DECO_IS_ROLL
	deco['~'] = "roll";
#endif
	deco['H'] = "fermata";
	deco['L'] = "emphasis";
	deco['M'] = "lowermordent";
	deco['O'] = "coda";
	deco['P'] = "uppermordent";
	deco['S'] = "segno";
	deco['T'] = "trill";
	deco['u'] = "upbow";
	deco['v'] = "downbow";

	/* non-standard */
#ifndef DECO_IS_ROLL
	deco['~'] = "gmark";
#endif
	deco['J'] = "slide";
	deco['R'] = "roll";
}

/* reset the decoration table at start of a new tune */
void reset_deco(void)
{
//	struct deco_def_s *dd;
//	int ideco;
//
//	for (ideco = 1, dd = &deco_def_tb[1]; ideco < 128; ideco++, dd++) {
//		if (!dd->name)
//			break;
//		free(dd->name);
//	}
	memset(deco_def_tb, 0, sizeof deco_def_tb);
}

/* -- set the decoration flags -- */
void set_defl(int new_defl)
{
	if (defl == new_defl)
		return;
	defl = new_defl;
	a2b("/defl %d def ", new_defl);
}
abcm2ps-8.5.2/deco.abc0000644000175000017500000002774612451313777012562 0ustar  jefjef%!! here is an experimental decoration extension !!
%!! syntax may change in next releases !!
%
% this option is required for !trem!
% %setdefl 1
%
% == postscript definitions - must be before any tune ==
%
%%beginps
% -- alternate piano/forte indication between parenthesis
/apf{		% usage: str x y apf
	M -6 5 RM
	/Times-Italic 16 selectfont(\()show
	/Times-BoldItalic 16 selectfont show
	/Times-Italic 16 selectfont(\))show}!
%
% -- draw parenthesis around a note
/opnot{		% usage: x y opnot
	M -5.5 -3 RM /Times-Roman 15 selectfont(\(   \))show}!
% -- same, but inside a chord (x is not the same)
/opchnot{	% usage: x y opchnot
	M -10.5 -3 RM /Times-Roman 15 selectfont(\(   \))show}!
%
% -- sforzando accent
/sfa{		% usage: x y sfa	
	1.2 SLW M -2.4 4.8 RM
	2.4 8.0 RL
	2.4 -7.4 RL
	-1.0 0.0 RL
	-1.8 6.4 RL
	stroke}!
%
% -- draw octava indication
/octava{	% usage: w x y octava
	exch -10 add exch 2 copy
	M 0 10 RM /Times-Roman 16 selectfont(8)show
	/Times-Roman 12 selectfont(va)show
	M 0 6 RL currentpoint stroke M
	[6] 0 setdash 30 add 0 RL currentpoint stroke M
	[] 0 setdash 0 -6 RL stroke}!
/octavab{	% usage: w x y octavab
	exch -14 add exch 2 copy 
	M 0 2 RM /Times-Roman 16 selectfont(8)show
	/Times-Roman 12 selectfont(va basso)show
	22 add M 0 -6 RL currentpoint stroke M
	[6] 0 setdash 30 add 0 RL stroke
	[] 0 setdash}!
%
% -- write big letters (position marks) above the staff
% (from Guido Gonzato)
/bigl{		% usage: str x y bigl
	/Times-Bold 26 selectfont
	4 add M showc
	1 SLW 1 -2 RM 
	0 22 RL -22 0 RL
	0 -22 RL 22 0 RL stroke}!
% (from Jonas Petersson)
/biglc{		% usage: str x y biglc
	2 copy 5 2 roll /Times-Bold 22 selectfont
	6 add M showc
	1 SLW 13 add newpath
	12 0 360 arc stroke}!
%
% -- pedal glyph
% (from CMN http://ccrma-www.stanford.edu/software/cmn/cmn/cmn.html)
/ped{		% usage: str x y ped
	gsave 4 add exch -10 add exch T 26 dup scale
	0.368 0.074 moveto
	0.341 0.121 0.335 0.147 0.371 0.203 curveto
	0.435 0.289 0.531 0.243 0.488 0.155 curveto
	0.472 0.117 0.434 0.096 0.414 0.080 curveto
	0.429 0.038 0.494 -0.006 0.541 0.075 curveto
	0.559 0.123 0.558 0.224 0.663 0.252 curveto
	0.603 0.354 0.449 0.393 0.461 0.405 curveto
	0.902 0.262 0.705 -0.124 0.555 0.046 curveto
	0.488 -0.032 0.417 0.021 0.389 0.055 curveto
	0.303 -0.018 0.303 -0.020 0.248 0.040 curveto
	0.218 0.108 0.191 0.062 0.164 0.047 curveto
	0.010 -0.056 0.032 0.019 0.124 0.062 curveto
	0.229 0.117 0.200 0.091 0.228 0.195 curveto
	0.240 0.241 0.149 0.250 0.166 0.311 curveto
	0.207 0.493 lineto
	-0.041 0.441 0.049 0.261 0.126 0.387 curveto
	0.138 0.381 lineto
	-0.020 0.119 -0.100 0.472 0.220 0.507 curveto
	0.548 0.486 0.399 0.171 0.254 0.374 curveto
	0.264 0.384 lineto
	0.338 0.259 0.521 0.449 0.228 0.488 curveto
	0.198 0.356 lineto
	0.181 0.304 0.273 0.294 0.262 0.241 curveto
	0.229 0.101 lineto
	0.273 0.070 0.282 -0.038 0.368 0.074 curveto
	0.391 0.094 moveto
	0.456 0.130 0.476 0.171 0.468 0.213 curveto
	0.452 0.276 0.333 0.171 0.391 0.094 curveto
	0.627 0.019 moveto
	0.533 0.041 0.586 0.228 0.678 0.229 curveto
	0.729 0.170 0.712 0.025 0.627 0.019 curveto
	eofill
	0.8 0.04 0.04 0 360 newpath arc fill
	pop grestore}!
%
% -- pedal off glyph
% (from CMN http://ccrma-www.stanford.edu/software/cmn/cmn/cmn.html)
/pedoff{	% usage: str x y ped
	gsave 4 add exch -5 add exch T 26 dup scale
	0.219 0.198 moveto
	0.231 0.172 0.195 0.138 0.162 0.173 curveto
	0.149 0.219 0.206 0.231 0.219 0.198 curveto
	0.144 0.242 moveto
	0.166 0.223 0.193 0.230 0.181 0.267 curveto
	0.178 0.306 0.144 0.302 0.151 0.335 curveto
	0.160 0.381 0.225 0.377 0.224 0.330 curveto
	0.228 0.302 0.198 0.306 0.197 0.267 curveto
	0.194 0.237 0.213 0.222 0.237 0.247 curveto
	0.263 0.276 0.234 0.297 0.268 0.322 curveto
	0.314 0.347 0.354 0.297 0.316 0.259 curveto
	0.296 0.237 0.273 0.266 0.246 0.237 curveto
	0.223 0.217 0.232 0.194 0.266 0.197 curveto
	0.303 0.202 0.302 0.232 0.332 0.228 curveto
	0.381 0.232 0.388 0.156 0.332 0.152 curveto
	0.302 0.148 0.302 0.185 0.266 0.183 curveto
	0.231 0.186 0.228 0.169 0.245 0.143 curveto
	0.273 0.116 0.297 0.141 0.316 0.117 curveto
	0.350 0.075 0.303 0.029 0.258 0.062 curveto
	0.237 0.082 0.261 0.102 0.233 0.133 curveto
	0.212 0.151 0.194 0.147 0.197 0.113 curveto
	0.203 0.075 0.232 0.075 0.230 0.043 curveto
	0.223 -0.004 0.159 -0.002 0.152 0.042 curveto
	0.148 0.075 0.185 0.076 0.183 0.113 curveto
	0.183 0.147 0.163 0.150 0.141 0.133 curveto
	0.113 0.104 0.140 0.079 0.113 0.059 curveto
	0.069 0.037 0.033 0.077 0.063 0.117 curveto
	0.082 0.141 0.104 0.117 0.132 0.142 curveto
	0.153 0.163 0.144 0.188 0.113 0.182 curveto
	0.073 0.182 0.075 0.147 0.046 0.152 curveto
	-0.003 0.152 -0.003 0.227 0.048 0.227 curveto
	0.075 0.231 0.075 0.198 0.113 0.196 curveto
	0.141 0.197 0.147 0.207 0.133 0.237 curveto
	0.102 0.264 0.082 0.237 0.062 0.261 curveto
	0.028 0.302 0.077 0.347 0.118 0.318 curveto
	0.138 0.297 0.116 0.275 0.144 0.242 curveto
	fill pop grestore}!
%
% -- glissendo
/gliss{	% usage: x2 y2 x1 y1 gliss
	gsave exch 13 add exch 2 copy T 0 0 M
	exch 4 -1 roll exch sub 3 1 roll sub	% dx dy
	2 copy exch atan dup rotate	% dx dy alpha
	exch pop cos div		% len
% 	9 sub 0 RL			% simple line
	9 sub 0 8 3 -1 roll{		% squiggly line
		2 -1.15 2.30 150 30 arcn 4 0 T
		2 1.15 2.30 -150 -30 arc 4 0 T pop
	}for
	1 SLW stroke grestore}!
%
% -- upper glissendo
/glissup{	% usage: x y glissup
	gsave T 5 0 T
	25 rotate 10 0 T 0 0 M
	0 8 8{
		2 -1.15 2.30 150 30 arcn 4 0 T
		2 1.15 2.30 -150 -30 arc 4 0 T pop
	}for
	1 SLW stroke grestore}!
%
% -- note decorations
% (sorry for I don't know the name of these: there so many ones)
% The convention I use here is:
%	- t2 or t3: mordent with 2 or 3 peeks
%	- ta or tb: turn from above or from below
%	- b, ub or db: middle, upper or lower bar
/tr3{		% usage: x y tr3 - mordent with 3 peeks
	M 2.2 2.2 RL 2.1 -2.9 RL 0.7 0.7 RL
	2.2 2.2 RL 2.1 -2.9 RL 0.7 0.7 RL
	2.2 2.2 RL 2.1 -2.9 RL 0.7 0.7 RL
	-2.2 -2.2 RL -2.1 2.9 RL -0.7 -0.7 RL
	-2.2 -2.2 RL -2.1 2.9 RL -0.7 -0.7 RL
	-2.2 -2.2 RL -2.1 2.9 RL -0.7 -0.7 RL fill}!
/t2ub{		% usage: x y t2ub - mordent ending with an upper bar
	2 copy umrd 0.6 SLW
	M 5 4 RM 0 6 RL stroke}!
/t3tab{		% usage: x y t3tab - mordent + upper turn and bar
	4 add 2 copy exch 7.5 sub exch tr3 exch 7.5 add exch
	2 copy 0.6 SLW M 2 6 14 6 16 0 RC
	M 8 1 RM 0 6 RL stroke}!
/ubt3ta{	% usage: x y ubt3ta - up bar + mordent + upper turn
	4 add 2 copy 0.6 SLW
	M -7.5 0 RM 0 6 RL stroke
	2 copy exch 7.5 sub exch tr3
	M 7.5 0 RM 2 6 14 6 16 0 RC stroke}!
/tbt3{		% usage: x y tbt3 - low turn + long mordent
	exch 10 sub exch 6 add 2 copy 0.6 SLW
	M -8 0 RM 2 -6 14 -6 16 0 RC stroke
	exch 8 add exch tr3}!
/t2ta{		% usage: x y t2ta - mordent + upper turn
	2 copy umrd
	M 5 4 RM 1 5 9 5 10 0 RC stroke}!
/t3b{		% usage: x y t3b - upper + lower mordent
	2 copy exch -7.5 add exch 4 add tr3 0.6 SLW
	M 2.5 0 RM 0 8 RL stroke}!
/sharp{		% usage: x y sharp - sharp above note
	4 add gsave T 0.6 dup scale 0 0 sh0 grestore}!
/flat{		% usage: x y flat - flat above note
	4 add gsave T 0.6 dup scale 0 0 ft0 grestore}!
/natural{	% usage: x y natural - natural sign above note
	4 add gsave T 0.6 dup scale 0 0 nt0 grestore}!
%
% -- 'treble-8' customization
/octl{
	/Times-BoldItalic 16 selectfont M 5.5 -14 RM(8vb)show}!
%
% -- latin guitar chords
% note: 'Ré' cannot be used
/gcshow{
	dup 0 get
	dup dup 65 ge exch 71 le and{
		65 sub[(La)(Si)(Do)(Re)(Mi)(Fa)(Sol )]exch get show
		dup length 1 sub 1 exch getinterval
	}if show}!
%
% -- 'tr' + long trill
/trtrill{	% usage: w x y trtrill
	2 copy trl
	3 1 roll 9 add 3 1 roll 9 sub 3 1 roll 2 add ltr}!
%
% -- guitar diagrams
/guitar{	% usage: x y guitar
	gsave exch 10 sub exch 8 add T
	1.5 SLW -0.3 24.6 M 20.6 0 RL stroke
	0.6 SLW 0 0 M 20 0 RL 0 6 M 20 0 RL
	0 12 M 20 0 RL 0 18 M 20 0 RL
	0 0 M 0 24 RL 4 0 M 0 24 RL 8 0 M 0 24 RL
	12 0 M 0 24 RL 16 0 M 0 24 RL 20 0 M 0 24 RL
	stroke 0.5 SLW}!
/gdot{newpath 1.4 0 360 arc fill}!
/gx{28 M -1.3 -1.3 RM 2.6 2.6 RL 0 -2.6 RM -2.6 2.6 RL stroke}!
/go{28 newpath 1.5 0 360 arc stroke}!
/Dm{		% usage: x y Dm
	guitar
	0 gx 4 gx 8 go
	20 21 gdot 12 15 gdot 16 9 gdot
	grestore}!
/Bb{
	guitar
	0 gx 20 gx
	4 21 gdot 8 9 gdot 12 9 gdot 16 9 gdot
	grestore}!
/C7{
	guitar
	0 gx 20 go
	16 21 gdot 8 15 gdot 4 9 gdot 12 9 gdot
	grestore}!
%
% -- arpeggio variations
%	arpeggio with arrow 1st version
/arpu{	2 copy 4 index add M -6.5 0 RM 2.5 5 RL 2.5 -5 RL fill
	arp}!
/arpd{	2 copy M -6.5 0 RM 2.5 -5 RL 2.5 5 RL fill
	arp}!
%	arpeggio with arrow other version
% /arpu{	2 copy 4 index add M -7 0 RM 3 5 RL 3 -5 RL
%	0.7 SLW stroke arp}!
% /arpd{	2 copy M -7 0 RM 3 -5 RL 3 5 RL
%	0.7 SLW stroke arp}!
%	arpeggio crossing the staves
% (! this works because the decoration are drawn sorted by time !)
/arps{		% arpeggio start - stack: h x ylow
	exch /x exch def	% memorize 'x'
	add /y exch def}!	% memorize the upper vert offset
/arpe{		% arpeggio end - stack: h x ylow
	3 -1 roll pop		% discard 'h'
	exch dup x gt {pop x} if exch	% have room for accidentals
	y 1 index sub 3 1 roll	% new height
	arp}!
%
% -- optional breath
/brth{6 add /xbr 2 index def /ybr 1 index def
	/Times-BoldItalic 30 selectfont M (,) show}!
/opbrth{pop pop xbr 10 sub ybr 5 sub
	/Times-Roman 20 selectfont M (\(  \)) show}!
%
% -- head decorations / replacement
% lower mordent
/hlmrd{		% usage: x y hlmrd
	exch 12 sub exch 4 sub lmrd}!
% small note head
/shd{		% usage: x y shd
	M 3 1.3 RM
	-1 2.5 -6.5 0 -5.5 -2.5 RC
	1 -2.5 6.5 0 5.5 2.5 RC fill}!
% -- measure bar between two staves (1 and 2)
/hbar{		% usage: x y hbar
	dlw pop dup 0 y0 M 24 y1 lineto stroke}!
%%endps
%
% == decoration definitions ==
%
% actual syntax (see http://moinejf.free.fr/abcm2ps-doc/index.html):
%	%%deco <name> <c_func> <ps_func> <h> <wl> <wr> [<str>]
%
% -- accent near the note / sforzando
%%deco accn 0 accent 8 4 4
%%deco sfa 3 sfa 12 5 5
%
% -- dynamic indication below the staff
%%deco fp 6 pf 18 5 11 fp
%%deco cresc 6 crdc 20 10 22 Cresc.
%%deco decresc 6 crdc 20 10 26 Decresc.
%%deco dimin 6 crdc 20 10 22 Dimin.
%%deco riten 6 crdc 20 12 34 Poco riten.
%
% -- dynamic indication below the staff between parenthesis
%%deco (p) 6 apf 20 8 16 p
%%deco (pp) 6 apf 20 8 24 pp
%%deco (f) 6 apf 20 8 16 f
%%deco (ff) 6 apf 20 8 24 ff
%
% -- repeat indication above the staff
%%deco alcoda 3 dacs 20 0 0 al Coda
%
% -- who asked for a Pedal indication ?
%%deco ped 6 ped 20 0 0
%%deco ped-end 6 pedoff 20 0 0
%
% -- optional note
%%deco () 1 opnot 0 0 0
%
% -- start / stop of octava indication
%%deco 8( 5 - 24 0 0
%%deco 8) 5 octava 24 0 0
%%deco 8b( 7 - 24 0 0
%%deco 8b) 7 octavab 24 0 0
%
% -- big letters
%%deco biga 3 bigl 20 0 0 A
%%deco bigb 3 biglc 20 0 0 B
% ...
%
% -- glissendo
%%deco -( 1 - 0 0 0
%%deco -) 1 gliss 0 0 0
%%deco - 1 glissup 0 2 10
%
% -- note decorations
%%deco t2ub 3 t2ub 12 5 5
%%deco t3tab 3 t3tab 12 8 24
%%deco ubt3ta 3 ubt3ta 12 8 24
%%deco tbt3 3 tbt3 14 14 18
%%deco t2ta 3 t2ta 12 5 15
%%deco t3b 3 t3b 12 5 15
%%deco # 3 sharp 8 0 0
%%deco b 3 flat 8 0 0
%%deco = 3 natural 8 0 0
%
% -- 'tr' + long trill
%%deco tr( 5 - 11 0 0
%%deco tr) 5 trtrill 11 0 0
%
% -- guitar chords
%%deco Dm 3 Dm 36 0 0
%%deco Bb 3 Bb 36 0 0
%%deco C7 3 C7 36 0 0
%
% -- arpeggios
%%deco arpu 2 arpu 0 0 0
%%deco arpd 2 arpd 0 0 0
%%deco arps 2 arps 0 0 0
%%deco arpe 2 arpe 0 0 0
%
% -- optional breath
%%deco opbrth 3 opbrth 0 0 0
%
% -- head decorations
%%deco op 1 opchnot 0 0 0	% optional head
%%deco m 0 hlmrd 0 0 0		% lower mordent on the left
%%deco head-x 0 dsh0 0 0 0	% X head
%%deco head-shd 0 shd 0 0 0	% small head
%
% -- measure bar between two staves
%%deco hbar 3 hbar 0 0 0

X:1
T:Customized decorations
M:C
L:1/8
K:C
!biga!y!fp!"C"C!t2ub!C !cresc!"D"D!t3tab!D !decresc!"E"E!ubt3ta!E !dimin!"F"F!tbt3!F|\
!mp!"G"G!t2ta!G !(p)!A!t3b!c T!b!!(pp)!A2 P!#!!(f)!B2|M!=!!(ff)!c8|
%
!8(!!riten!EF !-!G2 !ped!GA!ped-end!B!8)!c|!8b(!!bigb!C2!()!E2 !tr(!G3!tr)!!8b)!c!alcoda!|\
CE!-(!G2!-)!C2c2|
%
"Dm"!Dm!e3/d/ d6 | "Bb"!Bb!z2 d/d3/ "C7"!C7!cB/A/- AG |\
F!accent!G!accn!AB !sfa!c4||

X:2
T:Decorations with abcm2ps 4.x.x
M:C
L:1/4
%%staves {1 2}
K:C
V:1
!arpu![!op!C!op!Gc]4 !hbar![] !breath!!opbrth!!arpd![CGc]4 |\
	!arps![CGc]4 | z3/[!head-x!B]/ [c!m!eg]2 | c4 ||
V:2
!arpeggio![C,,G,,C,]4 [] [C,,G,,C,]4 |\
	!arpe![C,,G,,C,]4 | [C,,!head-shd!C,][G,,!head-shd!G,]C,2 | C,4 ||
abcm2ps-8.5.2/draw.c0000644000175000017500000033123112462105304012250 0ustar  jefjef/*
 * Drawing functions.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2015 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.
 */

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

#include "abc2ps.h"

struct BEAM {			/* packages info on one beam */
	struct SYMBOL *s1, *s2;
	float a, b;
	short nflags;
};

static char *acc_tb[] = { "", "sh", "nt", "ft", "dsh", "dft" };

/* scaling stuff */
static int scale_voice;		/* staff (0) or voice(1) scaling */
static float cur_scale = 1;	/* voice or staff scale */
static float cur_trans = 0;	/* != 0 when scaled staff */
static float cur_staff = 1;	/* current scaled staff */
static int cur_color = 0;	/* current color */

static void draw_note(float x,
		      struct SYMBOL *s,
		      int fl);
static void set_tie_room(void);

/* set the staff / voice color */
void set_st_color(int st)
{
	int old_color = cur_color;

	if (st < 0)
		cur_color = 0;
	else
		cur_color = staff_tb[st].color;
	if (cur_color != old_color)
		a2b("%.2f %.2f %.2f setrgbcolor ",
			(float) (cur_color >> 16) / 255,
			(float) ((cur_color >> 8) & 0xff) / 255,
			(float) (cur_color & 0xff) / 255);
}

void set_v_color(int v)
{
	int old_color = cur_color;

	if (v < 0)
		cur_color = 0;
	else
		cur_color = voice_tb[v].color;
	if (cur_color != old_color)
		a2b("%.2f %.2f %.2f setrgbcolor ",
			(float) (cur_color >> 16) / 255,
			(float) ((cur_color >> 8) & 0xff) / 255,
			(float) (cur_color & 0xff) / 255);
}

/* output debug annotations */
static void anno_out(struct SYMBOL *s, char type)
{
	if (s->as.linenum == 0)
		return;
	if (mbf[-1] != '\n')
		*mbf++ = '\n';
	a2b("%%A %c %d %d ", type, s->as.linenum, s->as.colnum);
	putxy(s->x - s->wl - 2, staff_tb[s->staff].y + s->ymn - 2);
	if (type != 'b' && type != 'e')		/* if not beam */
		a2b("%.1f %d", s->wl + s->wr + 4, s->ymx - s->ymn + 4);
	a2b("\n");
}

/* -- up/down shift needed to get k*6 -- */
static float rnd6(float y)
{
	int iy;

	iy = ((int) (y + 2.999) + 12) / 6 * 6 - 12;
	return iy - y;
}

/* -- compute the best vertical offset for the beams -- */
static float b_pos(int grace,
		   int stem,
		   int flags,
		   float b)
{
	float d1, d2, shift, depth;
	float top, bot;

	shift = !grace ? BEAM_SHIFT : 3;
	depth = !grace ? BEAM_DEPTH : 1.7;
	if (stem > 0) {
		bot = b - (flags - 1) * shift - depth;
		if (bot > 26)
			return 0;
		top = b;
	} else {
		top = b + (flags - 1) * shift + depth;
		if (top < -2)
			return 0;
		bot = b;
	}

	d1 = rnd6(top - BEAM_OFFSET);
	d2 = rnd6(bot + BEAM_OFFSET);
	if (d1 * d1 > d2 * d2)
		return d2;
	return d1;
}

/* duplicate a note for beaming continuation */
static struct SYMBOL *sym_dup(struct SYMBOL *s_orig)
{
	struct SYMBOL *s;

	s = (struct SYMBOL *) getarena(sizeof *s);
	memcpy(s, s_orig, sizeof *s);
	s->as.flags |= ABC_F_INVIS;
	s->as.text = NULL;
	memset(s->as.u.note.sl1, 0, sizeof s->as.u.note.sl1);
	memset(&s->as.u.note.dc, 0, sizeof s->as.u.note.dc);
	s->gch = NULL;
	s->ly = NULL;
	return s;
}

/* -- calculate a beam -- */
/* (the staves may be defined or not) */
static int calculate_beam(struct BEAM *bm,
			  struct SYMBOL *s1)
{
	struct SYMBOL *s, *s2;
	int notes, nflags, staff, voice, two_staves, two_dir;
	float x, y, ys, a, b, max_stem_err;
	float sx, sy, sxx, sxy, syy, a0, stem_xoff, scale;
	static float min_tb[2][6] = {
		{STEM_MIN, STEM_MIN,
			STEM_MIN2, STEM_MIN3, STEM_MIN4, STEM_MIN4},
		{STEM_CH_MIN, STEM_CH_MIN,
			STEM_CH_MIN2, STEM_CH_MIN3, STEM_CH_MIN4, STEM_CH_MIN4}
	};

	/* must have one printed note head */
	if (s1->as.flags & ABC_F_INVIS) {
		if (!s1->next
		 || (s1->next->as.flags & ABC_F_INVIS))
			return 0;
	}

	if (!(s1->sflags & S_BEAM_ST)) {	/* beam from previous music line */
		s = sym_dup(s1);
		s->prev = s1->prev;
		if (s->prev)
			s->prev->next = s;
		else
			voice_tb[s->voice].sym = s;
		s1->prev = s;
		s->next = s1;
		s->ts_prev = s1->ts_prev;
//		if (s->ts_prev)
			s->ts_prev->ts_next = s;
		s1->ts_prev = s;
		s->ts_next = s1;
		for (s2 = s->ts_prev; /*s2*/; s2 = s2->ts_prev) {
			switch (s2->type) {
			default:
				continue;
			case CLEF:
			case KEYSIG:
			case TIMESIG:
				break;
			}
			break;
		}
		s->x -= 12;
		if (s->x > s2->x + 12)
			s->x = s2->x + 12;
		s->sflags &= S_SEQST;
		s->sflags |= S_BEAM_ST | S_TEMP;
		s->as.u.note.slur_st = 0;
		s->as.u.note.slur_end = 0;
		s1 = s;
	}

	/* search last note in beam */
	notes = nflags = 0;	/* set x positions, count notes and flags */
	two_staves = two_dir = 0;
	staff = s1->staff;
	voice = s1->voice;
	stem_xoff = (s1->as.flags & ABC_F_GRACE) ? GSTEM_XOFF : STEM_XOFF;
	for (s2 = s1; ; s2 = s2->next) {
		if (s2->as.type == ABC_T_NOTE) {
			if (s2->nflags > nflags)
				nflags = s2->nflags;
			notes++;
			if (s2->staff != staff)
				two_staves = 1;
			if (s2->stem != s1->stem)
				two_dir = 1;
			if (s2->sflags & S_BEAM_END)
				break;
		}
		if (!s2->next) {		/* beam towards next music line */
			for (; ; s2 = s2->prev) {
				if (s2->as.type == ABC_T_NOTE)
					break;
			}
			s = sym_dup(s2);
			s->next = s2->next;
			if (s->next)
				s->next->prev = s;
			s2->next = s;
			s->prev = s2;
			s->ts_next = s2->ts_next;
			if (s->ts_next)
				s->ts_next->ts_prev = s;
			s2->ts_next = s;
			s->ts_prev = s2;
			s->sflags &= S_SEQST;
			s->sflags |= S_BEAM_END | S_TEMP;
			s->as.u.note.slur_st = 0;
			s->as.u.note.slur_end = 0;
			s->x += 12;
			if (s->x < realwidth - 12)
				s->x = realwidth - 12;
			s2 = s;
			notes++;
			break;
		}
	}
	bm->s2 = s2;			/* (don't display the flags) */
	if (staff_tb[staff].y == 0) {	/* staves not defined */
		if (two_staves)
			return 0;
	} else {			/* staves defined */
		if (!two_staves && !(s1->as.flags & ABC_F_GRACE)) {
			bm->s1 = s1;	/* beam already calculated */
if (s1->xs == s2->xs)
 bug("beam with null length", 1);
			bm->a = (s1->ys- s2->ys) / (s1->xs - s2->xs);
			bm->b = s1->ys - s1->xs * bm->a
				+ staff_tb[staff].y;
			bm->nflags = nflags;
			return 1;
		}
	}

	sx = sy = sxx = sxy = syy = 0;	/* linear fit through stem ends */
	for (s = s1; ; s = s->next) {
		if (s->as.type != ABC_T_NOTE)
			continue;
		if ((scale = voice_tb[s->voice].scale) == 1)
			scale = staff_tb[s->staff].staffscale;
		if (s->stem >= 0)
			x = stem_xoff + s->shhd[0];
		else
			x = -stem_xoff + s->shhd[s->nhd];
		x *= scale;
		x += s->x;
		s->xs = x;
		y = s->ys + staff_tb[s->staff].y;
		sx += x; sy += y;
		sxx += x * x; sxy += x * y; syy += y * y;
		if (s == s2)
			break;
	}

	/* beam fct: y=ax+b */
	a = (sxy * notes - sx * sy) / (sxx * notes - sx * sx);
	b = (sy - a * sx) / notes;

	/* the next few lines modify the slope of the beam */
	if (!(s1->as.flags & ABC_F_GRACE)) {
		if (notes >= 3) {
			float hh;

			hh = syy - a * sxy - b * sy;	/* flatten if notes not in line */
			if (hh > 0
			 && hh / (notes - 2) > .5)
				a *= BEAM_FLATFAC;
		}
		if (a >= 0)
			a = BEAM_SLOPE * a / (BEAM_SLOPE + a);	/* max steepness for beam */
		else
			a = BEAM_SLOPE * a / (BEAM_SLOPE - a);
	} else {
		if (a > BEAM_SLOPE)
			a = BEAM_SLOPE;
		else if (a < -BEAM_SLOPE)
			a = -BEAM_SLOPE;
	}

	/* to decide if to draw flat etc. use normalized slope a0 */
	a0 = a * (s2->xs - s1->xs) / (20 * (notes - 1));

	if (a0 * a0 < BEAM_THRESH * BEAM_THRESH)
		a = 0;			/* flat below threshhold */

	b = (sy - a * sx) / notes;	/* recalculate b for new slope */

/*  if (nflags>1) b=b+2*stem;*/	/* leave a bit more room if several beams */

	/* have flat beams when asked */
	if (cfmt.flatbeams) {
		if (!(s1->as.flags & ABC_F_GRACE))
			b = -11 + staff_tb[staff].y;
		else
			b = 35 + staff_tb[staff].y;
		a = 0;
	}

/*fixme: have a look again*/
	/* have room for the symbols in the staff */
	max_stem_err = 0;		/* check stem lengths */
	s = s1;
	if (two_dir) {				/* 2 directions */
/*fixme: more to do*/
		if (!(s1->as.flags & ABC_F_GRACE))
			ys = BEAM_SHIFT;
		else
			ys = 3;
		ys *= (nflags - 1);
		ys += BEAM_DEPTH;
		ys *= .5;
		if (s1->stem != s2->stem && s1->nflags < s2->nflags)
			ys *= s2->stem;
		else
			ys *= s1->stem;
		b += ys;
	} else if (!(s1->as.flags & ABC_F_GRACE)) {	/* normal notes */
		float stem_err, beam_h;

		beam_h = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1);
		while (s->ts_prev->as.type == ABC_T_NOTE
		    && s->ts_prev->time == s->time
		    && s->ts_prev->x > s1->xs)
			s = s->ts_prev;

		for (; s && s->time <= s2->time; s = s->ts_next) {
			if (s->as.type != ABC_T_NOTE
			 || (s->as.flags & ABC_F_INVIS)
			 || (s->staff != staff
			  && s->voice != voice)) {
				continue;
			}
			x = s->voice == voice ? s->xs : s->x;
			ys = a * x + b - staff_tb[s->staff].y;
			if (s->voice == voice) {
				if (s->nhd == 0)
					stem_err = min_tb[0][(unsigned) s->nflags];
				else
					stem_err = min_tb[1][(unsigned) s->nflags];
				if (s->stem > 0) {
					if (s->pits[s->nhd] > 26) {
						stem_err -= 2;
						if (s->pits[s->nhd] > 28)
							stem_err -= 2;
					}
					stem_err -= ys - (float) (3 * (s->pits[s->nhd] - 18));
				} else {
					if (s->pits[0] < 18) {
						stem_err -= 2;
						if (s->pits[0] < 16)
							stem_err -= 2;
					}
					stem_err -= (float) (3 * (s->pits[0] - 18)) - ys;
				}
				stem_err += BEAM_DEPTH + BEAM_SHIFT * (s->nflags - 1);
			} else {
/*fixme: KO when two_staves*/
				if (s1->stem > 0) {
					if (s->stem > 0) {
/*fixme: KO when the voice numbers are inverted*/
						if (s->ymn > ys + 4
						 || s->ymx < ys - beam_h - 2)
							continue;
						if (s->voice > voice)
							stem_err = s->ymx - ys;
						else
							stem_err = s->ymn + 8 - ys;
					} else {
						stem_err = s->ymx - ys;
					}
				} else {
					if (s->stem < 0) {
						if (s->ymx < ys - 4
						 || s->ymn > ys - beam_h - 2)
							continue;
						if (s->voice < voice)
							stem_err = ys - s->ymn;
						else
							stem_err = ys - s->ymx + 8;
					} else {
						stem_err = ys - s->ymn;
					}
				}
				stem_err += 2 + beam_h;
			}
			if (stem_err > max_stem_err)
				max_stem_err = stem_err;
		}
	} else {				/* grace notes */
		for ( ; ; s = s->next) {
			float stem_err;

			ys = a * s->xs + b - staff_tb[s->staff].y;
			stem_err = GSTEM - 2;
			if (s->stem > 0)
				stem_err -= ys - (float) (3 * (s->pits[s->nhd] - 18));
			else
				stem_err += ys - (float) (3 * (s->pits[0] - 18));
			stem_err += 3 * (s->nflags - 1);
			if (stem_err > max_stem_err)
				max_stem_err = stem_err;
			if (s == s2)
				break;
		}
	}

	if (max_stem_err > 0)		/* shift beam if stems too short */
		b += s1->stem * max_stem_err;

	/* have room for the gracenotes, bars and clefs */
/*fixme: test*/
    if (!two_staves && !two_dir)
	for (s = s1->next; ; s = s->next) {
		struct SYMBOL *g;

		switch (s->type) {
		case NOTEREST:		/* cannot move rests in multi-voices */
			if (s->as.type != ABC_T_REST)
				break;
			g = s->ts_next;
			if (!g || g->staff != staff
			 || g->type != NOTEREST)
				break;
//fixme:too much vertical shift if some space above the note
//fixme:this does not fix rest under beam in second voice (ts_prev)
			/*fall thru*/
		case BAR:
#if 1
			if (s->as.flags & ABC_F_INVIS)
#else
//??
			if (!(s->as.flags & ABC_F_INVIS))
#endif
				break;
			/*fall thru*/
		case CLEF:
			y = a * s->x + b;
			if (s1->stem > 0) {
				y = s->ymx - y
					+ BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
					+ 2;
				if (y > 0)
					b += y;
			} else {
				y = s->ymn - y
					- BEAM_DEPTH - BEAM_SHIFT * (nflags - 1)
					- 2;
				if (y < 0)
					b += y;
			}
			break;
		case GRACE:
			g = s->extra;
			for ( ; g; g = g->next) {
				if (g->type != NOTEREST)
					continue;
				y = a * g->x + b;
				if (s1->stem > 0) {
					y = g->ymx - y
						+ BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
						+ 2;
					if (y > 0)
						b += y;
				} else {
					y = g->ymn - y
						- BEAM_DEPTH - BEAM_SHIFT * (nflags - 1)
						- 2;
					if (y < 0)
						b += y;
				}
			}
			break;
		}
		if (s == s2)
			break;
	}

	if (a == 0)		/* shift flat beams onto staff lines */
		b += b_pos(s1->as.flags & ABC_F_GRACE, s1->stem, nflags,
				b - staff_tb[staff].y);

	/* adjust final stems and rests under beam */
	for (s = s1; ; s = s->next) {
		float dy;

		switch (s->as.type) {
		case ABC_T_NOTE:
			s->ys = a * s->xs + b - staff_tb[s->staff].y;
			if (s->stem > 0) {
				s->ymx = s->ys + 2.5;
#if 0
//fixme: hack
				if (s->ts_prev
				 && s->ts_prev->stem > 0
				 && s->ts_prev->staff == s->staff
				 && s->ts_prev->ymn < s->ymx
				 && s->ts_prev->x == s->x
				 && s->shhd[0] == 0) {
					s->ts_prev->x -= 5;	/* fix stem clash */
					s->ts_prev->xs -= 5;
				}
#endif
			} else {
				s->ymn = s->ys - 2.5;
			}
			break;
		case ABC_T_REST:
			y = a * s->x + b - staff_tb[s->staff].y;
			dy = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
				+ (s->head != H_FULL ? 4 : 9);
			if (s1->stem > 0) {
				y -= dy;
				if (s1->multi == 0 && y > 12)
					y = 12;
				if (s->y <= y)
					break;
			} else {
				y += dy;
				if (s1->multi == 0 && y < 12)
					y = 12;
				if (s->y >= y)
					break;
			}
			if (s->head != H_FULL) {
				int iy;

				iy = ((int) y + 3 + 12) / 6 * 6 - 12;
				y = iy;
			}
			s->y = y;
			break;
		}
		if (s == s2)
			break;
	}

	/* save beam parameters */
	if (staff_tb[staff].y == 0)	/* if staves not defined */
		return 0;
	bm->s1 = s1;
	bm->a = a;
	bm->b = b;
	bm->nflags = nflags;
	return 1;
}

/* -- draw a single beam -- */
/* (the staves are defined) */
static void draw_beam(float x1,
		      float x2,
		      float dy,
		      float h,
		      struct BEAM *bm,
		      int n)			/* beam number (1..n) */
{
	struct SYMBOL *s;
	float y1, dy2;

	s = bm->s1;
	if ((s->sflags & S_TREM2) && n > s->nflags - s->u
	 && s->head != H_EMPTY) {
		if (s->head >= H_OVAL) {
			x1 = s->x + 6;
			x2 = bm->s2->x - 6;
		} else {
			x1 += 5;
			x2 -= 6;
		}
	}

	y1 = bm->a * x1 + bm->b - dy;
	x2 -= x1;
	dy2 = bm->a * x2;

	putf(h);
	putx(x2);
	putf(dy2);
	putxy(x1, y1);
	a2b("bm\n");
}

/* -- draw the beams for one word -- */
/* (the staves are defined) */
static void draw_beams(struct BEAM *bm)
{
	struct SYMBOL *s, *s1, *s2;
	int i, beam_dir;
	float shift, bshift, bstub, bh, da;

	s1 = bm->s1;
/*fixme: KO if many staves with different scales*/
//fixme: useless?
//	set_scale(s1);
	s2 = bm->s2;
	if (!(s1->as.flags & ABC_F_GRACE)) {
		bshift = BEAM_SHIFT;
		bstub = BEAM_STUB;
		shift = .34;		/* (half width of the stem) */
		bh = BEAM_DEPTH;
	} else {
		bshift = 3;
		bstub = 3.2;
		shift = .29;
		bh = 1.6;
	}

/*fixme: quick hack for stubs at end of beam and different stem directions*/
	beam_dir = s1->stem;
	if (s1->stem != s2->stem
	 && s1->nflags < s2->nflags)
		beam_dir = s2->stem;
	if (beam_dir < 0)
		bh = -bh;
	if (cur_trans == 0 && cur_scale != 1) {
		bm->a /= cur_scale;
		bm->b = s1->ys - s1->xs * bm->a
			+ staff_tb[s1->staff].y;
		bshift *= cur_scale;
	}

	/* make first beam over whole word and adjust the stem lengths */
	draw_beam(s1->xs - shift, s2->xs + shift, 0., bh, bm, 1);
	da = 0;
	for (s = s1; ; s = s->next) {
		if (s->as.type == ABC_T_NOTE
		 && s->stem != beam_dir)
			s->ys = bm->a * s->xs + bm->b
				- staff_tb[s->staff].y
				+ bshift * (s->nflags - 1) * s->stem
				- bh;
		if (s == s2)
			break;
	}

	if (s1->sflags & S_FEATHERED_BEAM) {
		da = bshift / (s2->xs - s1->xs);
		if (s1->dur > s2->dur) {
			da = -da;
			bshift = da * s1->xs;
		} else {
			bshift = da * s2->xs;
		}
		da = da * beam_dir;
	}

	/* other beams with two or more flags */
	shift = 0;
	for (i = 2; i <= bm->nflags; i++) {
		shift += bshift;
		if (da != 0)
			bm->a += da;
		for (s = s1; ; s = s->next) {
			struct SYMBOL *k1, *k2;
			float x1;

			if (s->as.type != ABC_T_NOTE
			 || s->nflags < i) {
				if (s == s2)
					break;
				continue;
			}
			if ((s->sflags & S_TREM1)
			 && i > s->nflags - s->u) {
				if (s->head >= H_OVAL)
					x1 = s->x;
				else
					x1 = s->xs;
				draw_beam(x1 - 5, x1 + 5,
					  (shift + 2.5) * beam_dir,
					  bh, bm, i);
				if (s == s2)
					break;
				continue;
			}
			k1 = s;
			for (;;) {
				if (s == s2)
					break;
				if ((s->next->type == NOTEREST
				  && s->next->nflags - s->next->u < i)
				 || (s->next->sflags & S_BEAM_BR1)
				 || ((s->next->sflags & S_BEAM_BR2)
				  && i > 2))
					break;
				s = s->next;
			}
			k2 = s;
			while (k2->as.type != ABC_T_NOTE)
				k2 = k2->prev;
			x1 = k1->xs;
			if (k1 == k2) {
				if (k1 == s1) {
					x1 += bstub;
				} else if (k1 == s2) {
					x1 -= bstub;
				} else if ((k1->sflags & S_BEAM_BR1)
					|| ((k1->sflags & S_BEAM_BR2)
					 && i > 2)) {
					x1 += bstub;
				} else {
					struct SYMBOL *k;

					k = k1->next;
					while (k->as.type != ABC_T_NOTE)
						k = k->next;
					if ((k->sflags & S_BEAM_BR1)
					 || ((k->sflags & S_BEAM_BR2)
					  && i > 2)) {
						x1 -= bstub;
					} else {
						k1 = k1->prev;
						while (k1->as.type != ABC_T_NOTE)
							k1 = k1->prev;
						if (k1->nflags < k->nflags
						 || (k1->nflags == k->nflags
						  && k1->dots < k->dots))
							x1 += bstub;
						else
							x1 -= bstub;
					}
				}
			}
			draw_beam(x1, k2->xs,
#if 1
				  shift * beam_dir,
#else
				  shift * k1->stem,	/*fixme: more complicated */
#endif
				  bh, bm, i);
			if (s == s2)
				break;
		}
	}
	if (s1->sflags & S_TEMP)
		unlksym(s1);
	else if (s2->sflags & S_TEMP)
		unlksym(s2);
}

/* -- draw a system brace or bracket -- */
static void draw_sysbra(float x, int staff, int flag)
{
	int i, end;
	float yt, yb;

	while (cursys->staff[staff].empty
	    || staff_tb[staff].stafflines == 0) {
		if (cursys->staff[staff].flags & flag)
			return;
		staff++;
	}
	i = end = staff;
	for (;;) {
		if (!cursys->staff[i].empty
		 && staff_tb[i].stafflines != 0)
			end = i;
		if (cursys->staff[i].flags & flag)
			break;
		i++;
	}
	yt = staff_tb[staff].y + staff_tb[staff].topbar
				* staff_tb[staff].staffscale;
	yb = staff_tb[end].y + staff_tb[end].botbar
				* staff_tb[end].staffscale;
	a2b("%.1f %.1f %.1f %s\n",
	     yt - yb, x, yt,
	     (flag & (CLOSE_BRACE | CLOSE_BRACE2)) ? "brace" : "bracket");
}

/* -- draw the left side of the staves -- */
static void draw_lstaff(float x)
{
	int i, j, l, nst;
	float yb;

	if (cfmt.alignbars)
		return;
	nst = cursys->nstaff;
	l = 0;
	for (i = 0; ; i++) {
		if (cursys->staff[i].flags & (OPEN_BRACE | OPEN_BRACKET))
			l++;
		if (!cursys->staff[i].empty
		 && staff_tb[i].stafflines != 0)
			break;
		if (cursys->staff[i].flags & (CLOSE_BRACE | CLOSE_BRACKET))
			l--;
		if (i == nst)
			break;
	}
	for (j = nst; j > i; j--) {
		if (!cursys->staff[j].empty
		 && staff_tb[j].stafflines != 0)
			break;
	}
	if (i == j && l == 0)
		return;
	set_sscale(-1);
	yb = staff_tb[j].y + staff_tb[j].botbar
				* staff_tb[j].staffscale;
	a2b("%.1f %.1f %.1f bar\n",
	     staff_tb[i].y
		+ staff_tb[i].topbar * staff_tb[i].staffscale
		- yb,
	     x, yb);
	for (i = 0; i <= nst; i++) {
		if (cursys->staff[i].flags & OPEN_BRACE)
			draw_sysbra(x, i, CLOSE_BRACE);
		if (cursys->staff[i].flags & OPEN_BRACKET)
			draw_sysbra(x, i, CLOSE_BRACKET);
		if (cursys->staff[i].flags & OPEN_BRACE2)
			draw_sysbra(x - 6, i, CLOSE_BRACE2);
		if (cursys->staff[i].flags & OPEN_BRACKET2)
			draw_sysbra(x - 6, i, CLOSE_BRACKET2);
	}
}

/* -- draw a staff -- */
static void draw_staff(int staff,
			float x1, float x2)
{
	int nlines;
	float y;

	/* draw the staff */
	set_sscale(staff);
	y = staff_tb[staff].y;
	nlines = staff_tb[staff].stafflines;
	switch (nlines) {
	case 0:
		return;
	case 1:
		y += 12;
		break;
	case 2:
	case 3:
		y += 6;
		break;
	}
	putx(x2 - x1);
	a2b("%d ", nlines);
	putxy(x1, y);
	a2b("staff\n");
}

/* -- draw the time signature -- */
static void draw_timesig(float x,
			 struct SYMBOL *s)
{
	unsigned i, staff, l, l2;
	char *f, meter[64];
	float dx;

	if (s->as.u.meter.nmeter == 0)
		return;
	staff = s->staff;
	x -= s->wl;
	for (i = 0; i < s->as.u.meter.nmeter; i++) {
		l = strlen(s->as.u.meter.meter[i].top);
		if (l > sizeof s->as.u.meter.meter[i].top)
			l = sizeof s->as.u.meter.meter[i].top;
		if (s->as.u.meter.meter[i].bot[0] != '\0') {
			sprintf(meter, "(%.8s)(%.2s)",
				s->as.u.meter.meter[i].top,
				s->as.u.meter.meter[i].bot);
			f = "tsig";
			l2 = strlen(s->as.u.meter.meter[i].bot);
			if (l2 > sizeof s->as.u.meter.meter[i].bot)
				l2 = sizeof s->as.u.meter.meter[i].bot;
			if (l2 > l)
				l = l2;
		} else switch (s->as.u.meter.meter[i].top[0]) {
			case 'C':
				if (s->as.u.meter.meter[i].top[1] != '|') {
					f = "csig";
				} else {
					f = "ctsig";
					l--;
				}
				meter[0] = '\0';
				break;
			case 'c':
				if (s->as.u.meter.meter[i].top[1] != '.') {
					f = "imsig";
				} else {
					f = "iMsig";
					l--;
				}
				meter[0] = '\0';
				break;
			case 'o':
				if (s->as.u.meter.meter[i].top[1] != '.') {
					f = "pmsig";
				} else {
					f = "pMsig";
					l--;
				}
				meter[0] = '\0';
				break;
			case '(':
			case ')':
				sprintf(meter, "(\\%s)",
					s->as.u.meter.meter[i].top);
				f = "stsig";
				break;
			default:
				sprintf(meter, "(%.8s)",
					s->as.u.meter.meter[i].top);
				f = "stsig";
				break;
		}
		if (meter[0] != '\0')
			a2b("%s ", meter);
		dx = (float) (13 * l);
		putxy(x + dx * .5, staff_tb[staff].y);
		a2b("%s\n", f);
		x += dx;
	}
}

/* -- draw an accidental -- */
static void draw_acc(int acc, int microscale)
{
	int n, d;

	n = parse.micro_tb[acc >> 3];
	if (acc >> 3 != 0 && microscale) {
		if (microscale) {
			d = microscale;
			n = acc >> 3;
		} else {
			d = ((n & 0xff) + 1) * 2;
			n = (n >> 8) + 1;
		}
		a2b("%d %s%d ", n, acc_tb[acc & 0x07], d);
	} else {
		a2b("%s%d ", acc_tb[acc & 0x07], n);
	}
}

// draw helper lines
static void draw_hl(float x, int staffb, int up,
		int y, int stafflines, char *hltype)
{
	int i;

	if (!up) {
		switch (stafflines) {		// lower ledger lines
		case 0:
		case 1: i = 6; break;
		case 2:
		case 3: i = 0; break;
		default: i = -6; break;
		}
		for ( ; i >= y; i -= 6) {
			putxy(x, staffb + i);
			a2b("%s ", hltype);
		}
		return;
	}

	switch (stafflines) {			// upper ledger lines
	case 0:
	case 1:
	case 2: i = 18; break;
	case 3: i = 24; break;
	default: i = stafflines * 6; break;
	}
	for ( ; i <= y; i += 6) {
		putxy(x, staffb + i);
		a2b("%s ", hltype);
	}
}

/* -- draw a key signature -- */
static void draw_keysig(struct VOICE_S *p_voice,
			float x,
			struct SYMBOL *s)
{
	int old_sf = s->u;
	int staff = p_voice->staff;
	float staffb = staff_tb[staff].y;
	int i, clef_ix, shift;
	const signed char *p_seq;

	static const char sharp_cl[7] = {24, 9, 15, 21, 6, 12, 18};
	static const char flat_cl[7] = {12, 18, 24, 9, 15, 21, 6};
	static const signed char sharp1[6] = {-9, 12, -9, -9, 12, -9};
	static const signed char sharp2[6] = {12, -9, 12, -9, 12, -9};
	static const signed char flat1[6] = {9, -12, 9, -12, 9, -12};
	static const signed char flat2[6] = {-12, 9, -12, 9, -12, 9};

	clef_ix = s->pits[0];
	if (clef_ix & 1)
		clef_ix += 7;
	clef_ix /= 2;
	while (clef_ix < 0)
		clef_ix += 7;
	clef_ix %= 7;

	/* normal accidentals */
	if (s->as.u.key.nacc == 0 && !s->as.u.key.empty) {

		/* put neutrals if not 'accidental cancel' */
		if (cfmt.cancelkey || s->as.u.key.sf == 0) {

			/* when flats to sharps, or sharps to flats, */
			if (s->as.u.key.sf == 0
			 || old_sf * s->as.u.key.sf < 0) {

				/* old sharps */
				shift = sharp_cl[clef_ix];
				p_seq = shift > 9 ? sharp1 : sharp2;
				for (i = 0; i < old_sf; i++) {
					putxy(x, staffb + shift);
					a2b("nt0 ");
					shift += *p_seq++;
					x += 5.5;
				}

				/* old flats */
				shift = flat_cl[clef_ix];
				p_seq = shift < 18 ? flat1 : flat2;
				for (i = 0; i > old_sf; i--) {
					putxy(x, staffb + shift);
					a2b("nt0 ");
					shift += *p_seq++;
					x += 5.5;
				}
				if (s->as.u.key.sf != 0)
					x += 3;		/* extra space */

			/* or less sharps or flats */
			} else if (s->as.u.key.sf > 0) {	/* sharps */
				if (s->as.u.key.sf < old_sf) {
					shift = sharp_cl[clef_ix];
					p_seq = shift > 9 ? sharp1 : sharp2;
					for (i = 0; i < s->as.u.key.sf; i++)
						shift += *p_seq++;
					for (; i < old_sf; i++) {
						putxy(x, staffb + shift);
						a2b("nt0 ");
						shift += *p_seq++;
						x += 5.5;
					}
					x += 3;			/* extra space */
				}
			} else /*if (s->as.u.key.sf < 0)*/ {	/* flats */
				if (s->as.u.key.sf > old_sf) {
					shift = flat_cl[clef_ix];
					p_seq = shift < 18 ? flat1 : flat2;
					for (i = 0; i > s->as.u.key.sf; i--)
						shift += *p_seq++;
					for (; i > old_sf; i--) {
						putxy(x, staffb + shift);
						a2b("nt0 ");
						shift += *p_seq++;
						x += 5.5;
					}
					x += 3;			/* extra space */
				}
			}
		}

		/* new sharps */
		shift = sharp_cl[clef_ix];
		p_seq = shift > 9 ? sharp1 : sharp2;
		for (i = 0; i < s->as.u.key.sf; i++) {
			putxy(x, staffb + shift);
			a2b("sh0 ");
			shift += *p_seq++;
			x += 5.5;
		}

		/* new flats */
		shift = flat_cl[clef_ix];
		p_seq = shift < 18 ? flat1 : flat2;
		for (i = 0; i > s->as.u.key.sf; i--) {
			putxy(x, staffb + shift);
			a2b("ft0 ");
			shift += *p_seq++;
			x += 5.5;
		}
	} else {
		int acc, last_acc, last_shift;

		/* explicit accidentals */
		last_acc = s->as.u.key.accs[0];
		last_shift = 100;
		for (i = 0; i < s->as.u.key.nacc; i++) {
			acc = s->as.u.key.accs[i];
			shift = s->pits[0] * 3		// clef shift
				+ 3 * (s->as.u.key.pits[i] - 18);
			if (i != 0
			 && (shift > last_shift + 18
			  || shift < last_shift - 18))
				x -= 5.5;		// no clash
			else if (acc != last_acc)
				x += 3;
			last_acc = acc;
			if (shift < 0)
				draw_hl(x, staffb, 0,
					shift,		/* lower ledger line */
					staff_tb[s->staff].stafflines, "hl");
			else if (shift > 24)
				draw_hl(x, staffb, 1,
					shift,		/* upper ledger line */
					staff_tb[s->staff].stafflines, "hl");
			last_shift = shift;
			putxy(x, staffb + shift);
			draw_acc(acc, s->as.u.key.microscale);
			x += 5.5;
		}
	}
	if (old_sf != 0 || s->as.u.key.sf != 0 || s->as.u.key.nacc >= 0)
		a2b("\n");
}

/* -- convert the standard measure bars -- */
static int bar_cnv(int bar_type)
{
	switch (bar_type) {
	case B_OBRA:
/*	case B_CBRA: */
	case (B_OBRA << 4) + B_CBRA:
		return 0;			/* invisible */
	case B_COL:
		return B_BAR;			/* dotted */
#if 0
	case (B_CBRA << 4) + B_BAR:
		return B_BAR;
#endif
	case (B_BAR << 4) + B_COL:
		bar_type |= (B_OBRA << 8);		/* |: -> [|: */
		break;
	case (B_BAR << 8) + (B_COL << 4) + B_COL:
		bar_type |= (B_OBRA << 12);		/* |:: -> [|:: */
		break;
	case (B_BAR << 12) + (B_COL << 8) + (B_COL << 4) + B_COL:
		bar_type |= (B_OBRA << 16);		/* |::: -> [|::: */
		break;
	case (B_COL << 4) + B_BAR:
	case (B_COL << 8) + (B_COL << 4) + B_BAR:
	case (B_COL << 12) + (B_COL << 8) + (B_COL << 4) + B_BAR:
		bar_type <<= 4;
		bar_type |= B_CBRA;			/* :..| -> :..|] */
		break;
	case (B_COL << 4) + B_COL:
		bar_type = cfmt.dblrepbar;		/* :: -> dble repeat bar */
		break;
	}
	return bar_type;
}

/* -- draw a measure bar -- */
static void draw_bar(struct SYMBOL *s, float bot, float h)
{
	int staff, bar_type, dotted;
	float x, yb;
	char *psf;

	staff = s->staff;
	yb = staff_tb[staff].y;
	x = s->x;

	/* if measure repeat, draw the '%' like glyphs */
	if (s->as.u.bar.len != 0) {
		struct SYMBOL *s2;

		set_st_color(s->staff);
		set_scale(s);
		if (s->as.u.bar.len == 1) {
			for (s2 = s->prev; s2->as.type != ABC_T_REST; s2 = s2->prev)
				;
			putxy(s2->x, yb);
			a2b("mrep\n");
		} else {
			putxy(x, yb);
			a2b("mrep2\n");
			if (s->voice == cursys->top_voice) {
/*fixme				set_font(s->gcf); */
				set_font(cfmt.anf);
				putxy(x, yb + staff_tb[staff].topbar + 4);
				a2b("M(%d)showc\n", s->as.u.bar.len);
			}
		}
	}
	dotted = s->as.u.bar.dotted || s->as.u.bar.type == B_COL;
	bar_type = bar_cnv(s->as.u.bar.type);
	if (bar_type == 0)
		return;				/* invisible */
	for (;;) {
		psf = "bar";
		switch (bar_type & 0x07) {
		case B_BAR:
			if (dotted)
				psf = "dotbar";
			break;
		case B_OBRA:
		case B_CBRA:
			psf = "thbar";
			x -= 3;
			break;
		case B_COL:
			x -= 2;
			break;
		}
		switch (bar_type & 0x07) {
		default:
			set_sscale(-1);
			a2b("%.1f %.1f %.1f %s ", h, x, bot, psf);
			break;
		case B_COL:
			set_sscale(staff);
			putxy(x + 1, staff_tb[staff].y);
			a2b("rdots ");
			break;
		}
		bar_type >>= 4;
		if (bar_type == 0)
			break;
		x -= 3;
	}
	a2b("\n");
}

/* -- draw a rest -- */
/* (the staves are defined) */
static void draw_rest(struct SYMBOL *s)
{
	int i, y, no_head;
	float x, dotx, staffb;

static char *rest_tb[NFLAGS_SZ] = {
	"r128", "r64", "r32", "r16", "r8",
	"r4",
	"r2", "r1", "r0", "r00"
};

	/* if rest alone in the measure, center */
	if (s->dur == voice_tb[s->voice].meter.wmeasure) {
		struct SYMBOL *s2;

		/* don't use next/prev: there is no bar in voice averlay */
		s2 = s->ts_next;
		while (s2 && s2->time != s->time + s->dur)
			s2 = s2->ts_next;
		if (s2)
			x = s2->x;
		else
			x = realwidth;
		s2 = s;
		while (!(s2->sflags & S_SEQST))
			s2 = s2->ts_prev;
		s2 = s2->ts_prev;
		x = (x + s2->x) * .5;

		/* center the associated decorations */
		if (s->as.u.note.dc.n > 0)
			deco_update(s, x - s->x);
		s->x = x;
	} else {
		x = s->x + s->shhd[0] * cur_scale;
	}
	if (s->as.flags & ABC_F_INVIS)
//	 && !(s->sflags & S_OTHER_HEAD))	//fixme: before new deco struct
		return;

	staffb = staff_tb[s->staff].y;		/* bottom of staff */

	if (s->sflags & S_REPEAT) {
		putxy(x, staffb);
		if (s->doty < 0) {
			a2b("srep\n");
		} else {
			a2b("mrep\n");
			if (s->doty > 2
			 && s->voice == cursys->top_voice) {
/*fixme				set_font(s->gcf); */
				set_font(cfmt.anf);
				putxy(x, staffb + 24 + 4);
				a2b("M(%d)showc\n", s->doty);
			}
		}
		return;
	}

	y = s->y;

	no_head = 0;
	for (i = 0; i < s->as.u.note.dc.n; i++) {
		if (s->as.u.note.dc.tm[i].m == 0)
			no_head |= draw_deco_head(s->as.u.note.dc.tm[i].t,
						  x,
						  y + staffb,
						  s->stem);
	}
	if (no_head)
		return;

	i = C_XFLAGS - s->nflags;		/* rest_tb index */
	if (i == 7 && y == 12
	 && staff_tb[s->staff].stafflines <= 2)
		y -= 6;				/* semibreve a bit lower */

	putxy(x, y + staffb);				/* rest */
	a2b("%s ", rest_tb[i]);

	/* output ledger line(s) when greater than minim */
	if (i >= 6) {
		int yb, yt;

		switch (staff_tb[s->staff].stafflines) {
		case 0:
			yb = 12;
			yt = 12;
			break;
		case 1:
			yb = 6;
			yt = 18;
			break;
		case 2:
			yb = 0;
			yt = 18;
			break;
		case 3:
			yb = 0;
			yt = 24;
			break;
		default:
			yb = -6;
			yt = staff_tb[s->staff].stafflines * 6;
			break;
		}
		switch (i) {
		case 6:					/* minim */
			if (y <= yb || y >= yt) {
				putxy(x, y + staffb);
				a2b("hl ");
			}
			break;
		case 7:					/* semibreve */
			if (y < yb || y >= yt - 6) {
				putxy(x, y + 6 + staffb);
				a2b("hl ");
			}
			break;
		default:
			if (y < yb || y >= yt - 6) {
				putxy(x,y + 6 + staffb);
				a2b("hl ");
			}
			if (i == 9)			/* longa */
				y -= 6;
			if (y <= yb || y >= yt) {
				putxy(x, y + staffb);
				a2b("hl ");
			}
			break;
		}
	}

	dotx = 8;
	for (i = 0; i < s->dots; i++) {
		a2b("%.1f 3 dt ", dotx);
		dotx += 3.5;
	}
	a2b("\n");
}

/* -- draw grace notes -- */
/* (the staves are defined) */
static void draw_gracenotes(struct SYMBOL *s)
{
	int yy;
	float x0, y0, x1, y1, x2, y2, x3, y3, bet1, bet2, dy1, dy2;
	struct SYMBOL *g, *last;
	struct BEAM bm;

	/* draw the notes */
	bm.s2 = NULL;				/* (draw flags) */
	for (g = s->extra; g; g = g->next) {
		if (g->type != NOTEREST)
			continue;
		if ((g->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST) {
			if (annotate)
				anno_out(g, 'b');
			if (calculate_beam(&bm, g))
				draw_beams(&bm);
		}
		draw_note(g->x, g, bm.s2 == NULL);
		if (annotate)
			anno_out(s, 'g');
		if (g == bm.s2)
			bm.s2 = NULL;			/* (draw flags again) */

		if (g->as.flags & ABC_F_SAPPO) {	/* (on 1st note only) */
			if (!g->next) {			/* if one note */
				x1 = 9;
				y1 = g->stem > 0 ? 5 : -5;
			} else {			/* many notes */
				x1 = (g->next->x - g->x) * .5 + 4;
				y1 = (g->ys + g->next->ys) * .5 - g->y;
				if (g->stem > 0)
					y1 -= 1;
				else
					y1 += 1;
			}
			putxy(x1, y1);
			a2b("g%ca\n", g->stem > 0 ? 'u' : 'd');
		}
		if (annotate
		 && (g->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_END)
			anno_out(g, 'e');
		if (!g->next)
			break;			/* (keep the last note) */
	}

	/* slur */
	if (voice_tb[s->voice].key.mode >= BAGPIPE /* no slur when bagpipe */
	 || !cfmt.graceslurs
	 || s->as.u.note.slur_st		/* explicit slur */
	 || !s->next
	 || s->next->as.type != ABC_T_NOTE)
		return;
	last = g;
	if (last->stem >= 0) {
		yy = 127;
		for (g = s->extra; g; g = g->next) {
			if (g->type != NOTEREST)
				continue;
			if (g->y < yy) {
				yy = g->y;
				last = g;
			}
		}
		x0 = last->x;
		y0 = last->y - 5;
		if (s->extra != last) {
			x0 -= 4;
			y0 += 1;
		}
		s = s->next;
		x3 = s->x - 1;
		if (s->stem < 0)
			x3 -= 4;
		y3 = 3 * (s->pits[0] - 18) - 5;
		dy1 = (x3 - x0) * .4;
		if (dy1 > 3)
			dy1 = 3;
			dy2 = dy1;
		bet1 = .2;
		bet2 = .8;
		if (y0 > y3 + 7) {
			x0 = last->x - 1;
			y0 += .5;
			y3 += 6.5;
			x3 = s->x - 5.5;
			dy1 = (y0 - y3) * .8;
			dy2 = (y0 - y3) * .2;
			bet1 = 0;
		} else if (y3 > y0 + 4) {
			y3 = y0 + 4;
			x0 = last->x + 2;
			y0 = last->y - 4;
		}
	} else {
		yy = -127;
		for (g = s->extra; g; g = g->next) {
			if (g->type != NOTEREST)
				continue;
			if (g->y > yy) {
				yy = g->y;
				last = g;
			}
		}
		x0 = last->x;
		y0 = last->y + 5;
		if (s->extra != last) {
			x0 -= 4;
			y0 -= 1;
		}
		s = s->next;
		x3 = s->x - 1;
		if (s->stem >= 0)
			x3 -= 2;
		y3 = 3 * (s->pits[s->nhd] - 18) + 5;
		dy1 = (x0 - x3) * .4;
		if (dy1 < -3)
			dy1 = -3;
		dy2 = dy1;
		bet1 = .2;
		bet2 = .8;
		if (y0 < y3 - 7) {
			x0 = last->x - 1;
			y0 -= .5;
			y3 -= 6.5;
			x3 = s->x - 5.5;
			dy1 = (y0 - y3) * .8;
			dy2 = (y0 - y3) * .2;
			bet1 = 0;
		} else if (y3 < y0 - 4) {
			y3 = y0 - 4;
			x0 = last->x + 2;
			y0 = last->y + 4;
		}
	}

	x1 = bet1 * x3 + (1 - bet1) * x0;
	y1 = bet1 * y3 + (1 - bet1) * y0 - dy1;
	x2 = bet2 * x3 + (1 - bet2) * x0;
	y2 = bet2 * y3 + (1 - bet2) * y0 - dy2;

	a2b("%.2f %.2f %.2f %.2f %.2f %.2f ",
		x1 - x0, y1 - y0,
		x2 - x0, y2 - y0,
		x3 - x0, y3 - y0);
	putxy(x0, y0 + staff_tb[s->staff].y);
	a2b("gsl\n");
}

/* -- set the y offset of the dots -- */
static void setdoty(struct SYMBOL *s,
		    signed char *y_tb)
{
	int m, m1, y, doty;

	/* set the normal offsets */
	doty = s->doty;
	for (m = 0; m <= s->nhd; m++) {
		y = 3 * (s->pits[m] - 18);	/* note height on staff */
		if ((y % 6) == 0) {
			if (doty != 0)
				y -= 3;
			else
				y += 3;
		}
		y_tb[m] = y;
	}

	/* dispatch and recenter the dots in the staff space */
	for (m = 0; m < s->nhd; m++) {
		if (y_tb[m + 1] > y_tb[m])
			continue;
		m1 = m;
		while (m1 > 0) {
			if (y_tb[m1] > y_tb[m1 - 1] + 6)
				break;
			m1--;
		}
		if (3 * (s->pits[m1] - 18) - y_tb[m1]
				< y_tb[m + 1] - 3 * (s->pits[m + 1] - 18)) {
			while (m1 <= m)
				y_tb[m1++] -= 6;
		} else {
			y_tb[m + 1] = y_tb[m] + 6;
		}
	}
}

/* -- draw m-th head with accidentals and dots -- */
/* (the staves are defined) */
static void draw_basic_note(float x,
			    struct SYMBOL *s,
			    int m,
			    signed char *y_tb)
{
	int i, y, no_head, head, dots, nflags;
	float staffb, shhd;
	char *p;
	char perc_hd[8];

	staffb = staff_tb[s->staff].y;		/* bottom of staff */
	y = 3 * (s->pits[m] - 18);		/* note height on staff */
	shhd = s->shhd[m] * cur_scale;

	/* draw the decorations of the note heads */
	no_head = 0;
	for (i = 0; i < s->as.u.note.dc.n; i++) {
		if (s->as.u.note.dc.tm[i].m == m)
			no_head |= draw_deco_head(s->as.u.note.dc.tm[i].t,
						  x + shhd,
						  y + staffb,
						  s->stem);
	}
	if (s->as.flags & ABC_F_INVIS)
		return;

	/* special case when no head */
	if (s->nohdi1 >= 0
	 && m >= s->nohdi1 && m < s->nohdi2) {
		a2b("/x ");			/* set x y */
		putx(x + shhd);
		a2b("def/y ");
		puty(y + staffb);
		a2b("def");
		return;
	}

	identify_note(s, s->as.u.note.lens[m],
		      &head, &dots, &nflags);

	/* output a ledger line if horizontal shift / chord
	 * and note on a line */
	if (y % 6 == 0
	 && shhd != (s->stem > 0 ? s->shhd[0] : s->shhd[s->nhd])) {
		int yy;

		yy = 0;
		if (y >= 30) {
			yy = y;
			if (yy % 6)
				yy -= 3;
		} else if (y <= -6) {
			yy = y;
			if (yy % 6)
				yy += 3;
		}
		if (yy) {
			putxy(x + shhd, yy + staffb);
			a2b("hl ");
		}
	}

	/* draw the head */
	putxy(x + shhd, y + staffb);
	if (no_head) {
		p = "/y exch def/x exch def";
	} else if (s->as.flags & ABC_F_GRACE) {
		p = "ghd";
	} else if (s->type == CUSTOS) {
		p = "custos";
	} else if ((s->sflags & S_PERC)
	        && (i = s->as.u.note.accs[m]) != 0) {
		i &= 0x07;
		sprintf(perc_hd, "p%shd", acc_tb[i]);
		p = perc_hd;
	} else {
		switch (head) {
		case H_OVAL:
			if (s->as.u.note.lens[m] < BREVE) {
				p = "HD";
				break;
			}
			if (s->head != H_SQUARE) {
				p = "HDD";
				break;
			}
			/* fall thru */
		case H_SQUARE:
			if (s->as.u.note.lens[m] < BREVE * 2)
				p = "breve";
			else
				p = "longa";

			/* don't display dots on last note of the tune */
			if (!tsnext && s->next
			 && s->next->type == BAR && !s->next->next)
				dots = 0;
			break;
		case H_EMPTY:
			p = "Hd"; break;
		default:
			p = "hd"; break;
		}
	}
	a2b("%s", p);

	/* draw the dots */
/*fixme: to see for grace notes*/
	if (dots) {
		float dotx;
		int doty;

		dotx = 7.7 + s->xmx - shhd;
		doty = y_tb[m] - y;
		while (--dots >= 0) {
			a2b(" %.1f %d dt", dotx, doty);
			dotx += 3.5;
		}
	}

	/* draw the accidental */
	if ((i = s->as.u.note.accs[m]) != 0
	 && !(s->sflags & S_PERC)) {
		x -= s->shac[m] * cur_scale;
		a2b(" ");
		putx(x);
		if (s->as.flags & ABC_F_GRACE)
			a2b("gsc ");
		else
			a2b("y ");
		draw_acc(i, s->as.u.note.microscale);
		if (s->as.flags & ABC_F_GRACE)
			a2b(" grestore");
	}
}

/* -- draw a note or a chord -- */
/* (the staves are defined) */
static void draw_note(float x,
		      struct SYMBOL *s,
		      int fl)
{
	int m, ma;
	float staffb, slen, shhd;
	char c, *hltype;
	signed char y_tb[MAXHD];

	if (s->dots)
		setdoty(s, y_tb);
	if (s->head >= H_OVAL)
		x += 1;
	staffb = staff_tb[s->staff].y;

	/* output the ledger lines */
	if (!(s->as.flags & ABC_F_INVIS)) {
		if (s->as.flags & ABC_F_GRACE) {
			hltype = "ghl";
		} else {
			switch (s->head) {
			default:
				hltype = "hl";
				break;
			case H_OVAL:
				hltype = "hl1";
				break;
			case H_SQUARE:
				hltype = "hl2";
				break;
			}
		}
		shhd = (s->stem > 0 ? s->shhd[0] : s->shhd[s->nhd])
				* cur_scale;
		if (s->pits[0] < 22)
			draw_hl(x + shhd, staffb, 0,
				3 * (s->pits[0] - 18),	/* lower ledger lines */
				staff_tb[s->staff].stafflines, hltype);
		if (s->pits[s->nhd] > 22)
			draw_hl(x + shhd, staffb, 1,
				3 * (s->pits[s->nhd] - 18), /* upper ledger lines */
				staff_tb[s->staff].stafflines, hltype);
	}

	/* draw the master note, first or last one */
	if (cfmt.setdefl)
		set_defl(s->stem >= 0 ? DEF_STEMUP : 0);
	ma = s->stem >= 0 ? 0 : s->nhd;

	draw_basic_note(x, s, ma, y_tb);

	/* add stem and flags */
	if (!(s->as.flags & (ABC_F_INVIS | ABC_F_STEMLESS))) {
		char c2;

		c = s->stem >= 0 ? 'u' : 'd';
		slen = (s->ys - s->y) / voice_tb[s->voice].scale;
		if (!fl || s->nflags - s->u <= 0) {	/* stem only */
			c2 = (s->as.flags & ABC_F_GRACE) ? 'g' : 's';
			if (s->nflags > 0) {	/* (fix for PS low resolution) */
				if (s->stem >= 0)
					slen -= 1;
				else
					slen += 1;
			}
			a2b(" %.1f %c%c", slen, c2, c);
		} else {				/* stem and flags */
			if (cfmt.straightflags)
				c = 's';		/* straight flag */
			c2 = (s->as.flags & ABC_F_GRACE) ? 'g' : 'f';
			a2b(" %d %.1f s%c%c", s->nflags - s->u, slen, c2, c);
		}
	} else if (s->sflags & S_XSTEM) {	/* cross-staff stem */
		struct SYMBOL *s2;

		s2 = s->ts_prev;
		if (s2->stem > 0)
			slen = s2->y - s->y;
		else
			slen = s2->ys - s->y;
		slen += staff_tb[s2->staff].y - staffb;
/*fixme:KO when different scales*/
		slen /= voice_tb[s->voice].scale;
		a2b(" %.1f su", slen);
	}

	/* draw the tremolo bars */
	if (!(s->as.flags & ABC_F_INVIS)
	 && fl
	 && (s->sflags & S_TREM1)) {
		float x1;

		x1 = x;
		if (s->stem > 0)
			slen = 3 * (s->pits[s->nhd] - 18);
		else
			slen = 3 * (s->pits[0] - 18);
		if (s->head >= H_OVAL) {
			if (s->stem > 0)
				slen = slen + 5 + 5.4 * s->u;
			else
				slen = slen - 5 - 5.4;
		} else {
			x1 += ((s->as.flags & ABC_F_GRACE)
					? GSTEM_XOFF : STEM_XOFF)
							* s->stem;
			if (s->stem > 0)
				slen = slen + 6 + 5.4 * s->u;
			else
				slen = slen - 6 - 5.4;
		}
		slen /= voice_tb[s->voice].scale;
		a2b(" %d ", s->u);
		putxy(x1, staffb + slen);
		a2b("trem");
	}

	/* draw the other note heads */
	for (m = 0; m <= s->nhd; m++) {
		if (m == ma)
			continue;
		a2b(" ");
		draw_basic_note(x, s, m, y_tb);
	}
	a2b("\n");
}

/* -- find where to terminate/start a slur -- */
static struct SYMBOL *next_scut(struct SYMBOL *s)
{
	struct SYMBOL *prev;

	prev = s;
	for (s = s->next; s; s = s->next) {
		if (s->type == BAR
		 && ((s->sflags & S_RRBAR)
			|| s->as.u.bar.type == B_THIN_THICK
			|| s->as.u.bar.type == B_THICK_THIN
			|| (s->as.u.bar.repeat_bar
			 && s->as.text
			 && s->as.text[0] != '1')))
			return s;
		prev = s;
	}
	/*fixme: KO when no note for this voice at end of staff */
	return prev;
}

static struct SYMBOL *prev_scut(struct SYMBOL *s)
{
	while (s->prev) {
		s = s->prev;
		if (s->type == BAR
		 && ((s->sflags & S_RRBAR)
		  || s->as.u.bar.type == B_THIN_THICK
		  || s->as.u.bar.type == B_THICK_THIN
		  || (s->as.u.bar.repeat_bar
		   && s->as.text
		   && s->as.text[0] != '1')))
			return s;
	}

	/* return a symbol of any voice starting before the start of the voice */
	while (s->type != CLEF)
		s = s->ts_prev;		/* search a main voice */
	while (s->type == CLEF
	    || s->type == KEYSIG
	    || s->type == TIMESIG) {
		s = s->next;
	}
	return s->prev;
}

/* -- decide whether a slur goes up or down -- */
static int slur_direction(struct SYMBOL *k1,
			  struct SYMBOL *k2)
{
	struct SYMBOL *s;
	int some_upstem, low;

	some_upstem = low = 0;
	for (s = k1; ; s = s->next) {
		if (s->as.type == ABC_T_NOTE) {
			if (!(s->as.flags & ABC_F_STEMLESS)) {
				if (s->stem < 0)
					return 1;
				some_upstem = 1;
			}
			if (s->pits[0] < 22)	/* if under middle staff */
				low = 1;
		}
		if (s == k2)
			break;
	}
	if (!some_upstem && !low)
		return 1;
	return -1;
}

/* -- output a slur / tie -- */
static void slur_out(float x1,
		     float y1,
		     float x2,
		     float y2,
		     int s,
		     float height,
		     int dotted,
		     int staff)	/* if < 0, the staves are defined */
{
	float alfa, beta, mx, my, xx1, yy1, xx2, yy2, dx, dy, dz;
	float scale_y;

	alfa = .3;
	beta = .45;

	/* for wide flat slurs, make shape more square */
	dy = y2 - y1;
	if (dy < 0)
		dy = -dy;
	dx = x2 - x1;
	if (dx > 40. && dy / dx < .7) {
		alfa = .3 + .002 * (dx - 40.);
		if (alfa > .7)
			alfa = .7;
	}

	/* alfa, beta, and height determine Bezier control points pp1,pp2
	 *
	 *           X====alfa===|===alfa=====X
	 *	    /		 |	       \
	 *	  pp1		 |	        pp2
	 *	  /	       height		 \
	 *	beta		 |		 beta
	 *      /		 |		   \
	 *    p1		 m		     p2
	 *
	 */

	mx = .5 * (x1 + x2);
	my = .5 * (y1 + y2);

	xx1 = mx + alfa * (x1 - mx);
	yy1 = my + alfa * (y1 - my) + height;
	xx1 = x1 + beta * (xx1 - x1);
	yy1 = y1 + beta * (yy1 - y1);

	xx2 = mx + alfa * (x2 - mx);
	yy2 = my + alfa * (y2 - my) + height;
	xx2 = x2 + beta * (xx2 - x2);
	yy2 = y2 + beta * (yy2 - y2);

	dx = .03 * (x2 - x1);
//	if (dx > 10.)
//		dx = 10.;
//	dy = 1.6 * s;
	dy = 2 * s;
	dz = .2 + .001 * (x2 - x1);
	if (dz > .6)
		dz = .6;
	dz *= s;
	
	scale_y = scale_voice ? cur_scale : 1;
	if (!dotted)
		a2b("%.2f %.2f %.2f %.2f %.2f %.2f 0 %.2f ",
			(xx2 - dx - x2) / cur_scale,
				(yy2 + dy - y2 - dz) / scale_y,
			(xx1 + dx - x2) / cur_scale,
				(yy1 + dy - y2 - dz) / scale_y,
			(x1 - x2) / cur_scale,
				(y1 - y2 - dz) / scale_y,
				dz);
	a2b("%.2f %.2f %.2f %.2f %.2f %.2f ",
		(xx1 - x1) / cur_scale, (yy1 - y1) / scale_y,
		(xx2 - x1) / cur_scale, (yy2 - y1) / scale_y,
		(x2 - x1) / cur_scale, (y2 - y1) / scale_y);
	putxy(x1, y1);
	if (staff >= 0)
		a2b("y%d ", staff);
	a2b(dotted ? "dSL\n" : "SL\n");
}

/* -- check if slur sequence in a multi-voice staff -- */
static int slur_multi(struct SYMBOL *k1,
		      struct SYMBOL *k2)
{
	for (;;) {
		if (k1->multi != 0)	/* if multi voice */
			/*fixme: may change*/
			return k1->multi;
		if (k1 == k2)
			break;
		k1 = k1->next;
	}
	return 0;
}

/* -- draw a phrasing slur between two symbols -- */
/* (the staves are not yet defined) */
/* (not a pretty routine, this) */
static int draw_slur(struct SYMBOL *k1,
		     struct SYMBOL *k2,
		     int m1,
		     int m2,
		     int slur_type)
{
	struct SYMBOL *k;
	float x1, y1, x2, y2, height, addy;
	float a, y, z, h, dx, dy;
	int s, nn, upstaff, two_staves;

/*fixme: if two staves, may have upper or lower slur*/
	switch (slur_type & 0x03) {	/* (ignore dot bit) */
	case SL_ABOVE: s = 1; break;
	case SL_BELOW: s = -1; break;
	default:
		if ((s = slur_multi(k1, k2)) == 0)
			s = slur_direction(k1, k2);
		break;
	}

	nn = 1;
	upstaff = k1->staff;
	two_staves = 0;
	if (k1 != k2)
	    for (k = k1->next; k; k = k->next) {
		if (k->type == NOTEREST) {
			nn++;
			if (k->staff != upstaff) {
				two_staves = 1;
				if (k->staff < upstaff)
					upstaff = k->staff;
			}
		}
		if (k == k2)
			break;
	}
/*fixme: KO when two staves*/
if (two_staves) error(0, k1, "*** multi-staves slurs not treated yet");

	/* fix endpoints */
//	x1 = k1->x + k1->xmx;		/* take the max right side */
	x1 = k1->x + k1->shhd[0];
	if (k1 != k2) {
//		x2 = k2->x;
		x2 = k2->x + k2->shhd[0];
	} else {		/* (the slur starts on last note of the line) */
		for (k = k2->ts_next; k; k = k->ts_next)
			if (k->sflags & S_NEW_SY)
				break;
		if (!k)
			x2 = realwidth;
		else
			x2 = k->x;
	}
	y1 = (float) (s > 0 ? k1->ymx + 2 : k1->ymn - 2);
	y2 = (float) (s > 0 ? k2->ymx + 2 : k2->ymn - 2);

	if (k1->as.type == ABC_T_NOTE) {
		if (s > 0) {
			if (k1->stem > 0) {
				x1 += 5;
				if ((k1->sflags & S_BEAM_END)
				 && k1->nflags >= -1	/* if with a stem */
//fixme: check if at end of tuplet
				 && (!(k1->sflags & S_IN_TUPLET))) {
//				  || k1->ys > y1 - 3)) {
					if (k1->nflags > 0) {
						x1 += 2;
						y1 = k1->ys - 3;
					} else {
						y1 = k1->ys - 6;
					}
				} else {
					y1 = k1->ys + 3;
				}
			} else {
				y1 = k1->y + 8;
			}
		} else {
			if (k1->stem < 0) {
				x1 -= 1;
				if ((k1->sflags & S_BEAM_END)
				 && k1->nflags >= -1
				 && (!(k1->sflags & S_IN_TUPLET)
				  || k1->ys < y1 + 3)) {
					if (k1->nflags > 0) {
						x1 += 2;
						y1 = k1->ys + 3;
					} else {
						y1 = k1->ys + 6;
					}
				} else {
					y1 = k1->ys - 3;
				}
			} else {
				y1 = k1->y - 8;
			}
		}
	}

	if (k2->as.type == ABC_T_NOTE) {
		if (s > 0) {
			if (k2->stem > 0) {
				x2 += 1;
				if ((k2->sflags & S_BEAM_ST)
				 && k2->nflags >= -1
				 && (!(k2->sflags & S_IN_TUPLET)))
//					|| k2->ys > y2 - 3))
					y2 = k2->ys - 6;
				else
					y2 = k2->ys + 3;
			} else {
				y2 = k2->y + 8;
			}
		} else {
			if (k2->stem < 0) {
				x2 -= 5;
				if ((k2->sflags & S_BEAM_ST)
				 && k2->nflags >= -1
				 && (!(k2->sflags & S_IN_TUPLET)))
//					|| k2->ys < y2 + 3))
					y2 = k2->ys + 6;
				else
					y2 = k2->ys - 3;
			} else {
				y2 = k2->y - 8;
			}
		}
	}

	if (k1->as.type != ABC_T_NOTE) {
		y1 = y2 + 1.2 * s;
		x1 = k1->x + k1->wr * .5;
		if (x1 > x2 - 12)
			x1 = x2 - 12;
	}

	if (k2->as.type != ABC_T_NOTE) {
		if (k1->as.type == ABC_T_NOTE)
			y2 = y1 + 1.2 * s;
		else
			y2 = y1;
		if (k1 != k2)
			x2 = k2->x - k2->wl * .3;
	}

	if (nn >= 3) {
		if (k1->next->type != BAR
		 && k1->next->x < x1 + 48) {
			if (s > 0) {
				y = k1->next->ymx - 2;
				if (y1 < y)
					y1 = y;
			} else {
				y = k1->next->ymn + 2;
				if (y1 > y)
					y1 = y;
			}
		}
		if (k2->prev
		 && k2->prev->type != BAR
		 && k2->prev->x > x2 - 48) {
			if (s > 0) {
				y = k2->prev->ymx - 2;
				if (y2 < y)
					y2 = y;
			} else {
				y = k2->prev->ymn + 2;
				if (y2 > y)
					y2 = y;
			}
		}
	}

#if 0
	/* shift endpoints */
	addx = .04 * (x2 - x1);
	if (addx > 3.0)
		addx = 3.0;
	addy = .01 * (x2 - x1);
	if (addy > 3.0)
		addy = 3.0;
	x1 += addx;
	x2 -= addx;

/*fixme: to simplify*/
	if (k1->staff == upstaff)
		y1 += s * addy;
	else
		y1 = -6;
	if (k2->staff == upstaff)
		y2 += s * addy;
	else
		y2 = -6;
#endif

	a = (y2 - y1) / (x2 - x1);		/* slur steepness */
	if (a > SLUR_SLOPE || a < -SLUR_SLOPE) {
		if (a > SLUR_SLOPE)
			a = SLUR_SLOPE;
		else
			a = -SLUR_SLOPE;
		if (a * s > 0)
			y1 = y2 - a * (x2 - x1);
		else
			y2 = y1 + a * (x2 - x1);
	}

	/* for big vertical jump, shift endpoints */
	y = y2 - y1;
	if (y > 8)
		y = 8;
	else if (y < -8)
		y = -8;
	z = y;
	if (z < 0)
		z = -z;
	dx = .5 * z;
	dy = .3 * y;
	if (y * s > 0) {
		x2 -= dx;
		y2 -= dy;
	} else {
		x1 += dx;
		y1 += dy;
	}

	/* special case for grace notes */
	if (k1->as.flags & ABC_F_GRACE)
		x1 = k1->x - GSTEM_XOFF * .5;
	if (k2->as.flags & ABC_F_GRACE)
		x2 = k2->x + GSTEM_XOFF * 1.5;

	h = 0;
	a = (y2 - y1) / (x2 - x1);
	if (k1 != k2
	 && k1->voice == k2->voice) {
	    addy = y1 - a * x1;
	    for (k = k1->next; k != k2 ; k = k->next) {
		if (k->staff != upstaff)
			continue;
		switch (k->type) {
		case NOTEREST:
			if (s > 0) {
				y = 3 * (k->pits[k->nhd] - 18) + 6;
				if (y < k->ymx)
					y = k->ymx;
				y -= a * k->x + addy;
				if (y > h)
					h = y;
			} else {
				y = 3 * (k->pits[0] - 18) - 6;
				if (y > k->ymn)
					y = k->ymn;
				y -= a * k->x + addy;
				if (y < h)
					h = y;
			}
			break;
		case GRACE: {
			struct SYMBOL *g;

			for (g = k->extra; g; g = g->next) {
#if 1
				if (g->type != NOTEREST)
					continue;
				if (s > 0) {
					y = 3 * (g->pits[g->nhd] - 18) + 6;
					if (y < g->ymx)
						y = g->ymx;
					y -= a * g->x + addy;
					if (y > h)
						h = y;
				} else {
					y = 3 * (g->pits[0] - 18) - 6;
					if (y > g->ymn)
						y = g->ymn;
					y -= a * g->x + addy;
					if (y < h)
						h = y;
				}
#else
				y = g->y - a * k->x - addy;
				if (s > 0) {
					y += GSTEM + 2;
					if (y > h)
						h = y;
				} else {
					y -= 2;
					if (y < h)
						h = y;
				}
#endif
			}
			break;
		    }
		}
	    }
	    y1 += .45 * h;
	    y2 += .45 * h;
	    h *= .65;
	}

	if (nn > 3)
		height = (.08 * (x2 - x1) + 12) * s;
	else
		height = (.03 * (x2 - x1) + 8) * s;
	if (s > 0) {
		if (height < 3 * h)
			height = 3 * h;
		if (height > 40)
			height = 40;
	} else {
		if (height > 3 * h)
			height = 3 * h;
		if (height < -40)
			height = -40;
	}

	y = y2 - y1;
	if (y < 0)
		y = -y;
	if (s > 0) {
		if (height < .8 * y)
			height = .8 * y;
	} else {
		if (height > -.8 * y)
			height = -.8 * y;
	}
	height *= cfmt.slurheight;

/*fixme: ugly!*/
	if (m1 >= 0)
		y1 = (float) (3 * (k1->pits[m1] - 18) + 5 * s);
	if (m2 >= 0)
		y2 = (float) (3 * (k2->pits[m2] - 18) + 5 * s);

	slur_out(x1, y1, x2, y2, s, height, slur_type & SL_DOTTED, upstaff);

	/* have room for other symbols */
	dx = x2 - x1;
	a = (y2 - y1) / dx;
/*fixme: it seems to work with .4, but why?*/
	addy = y1 - a * x1 + .4 * height;
	if (k1->voice == k2->voice)
	    for (k = k1; k != k2; k = k->next) {
		if (k->staff != upstaff)
			continue;
		y = a * k->x + addy;
		if (k->ymx < y)
			k->ymx = y;
		else if (k->ymn > y)
			k->ymn = y;
		if (k->next == k2) {
			dx = x2;
			if (k2->sflags & S_SL1)
				dx -= 5;
		} else {
			dx = k->next->x;
		}
		if (k != k1)
			x1 = k->x;
		dx -= x1;
		y_set(upstaff, s > 0, x1, dx, y);
	}
	return (s > 0 ? SL_ABOVE : SL_BELOW) | (slur_type & SL_DOTTED);
}

/* -- draw the slurs between 2 symbols --*/
static void draw_slurs(struct SYMBOL *first,
		       struct SYMBOL *last)
{
	struct SYMBOL *s, *s1, *k, *gr1, *gr2;
	int i, m1, m2, gr1_out, slur_type, cont;

	gr1 = gr2 = NULL;
	s = first;
	for (;;) {
		if (!s || s == last) {
			if (!gr1
			 || !(s = gr1->next)
			 || s == last)
				break;
			gr1 = NULL;
		}
		if (s->type == GRACE) {
			gr1 = s;
			s = s->extra;
			continue;
		}
		if ((s->type != NOTEREST && s->type != SPACE)
		 || (s->as.u.note.slur_st == 0
			&& !(s->sflags & S_SL1))) {
			s = s->next;
			continue;
		}
		k = NULL;		/* find matching slur end */
		s1 = s->next;
		gr1_out = 0;
		for (;;) {
			if (!s1) {
				if (gr2) {
					s1 = gr2->next;
					gr2 = NULL;
					continue;
				}
				if (!gr1 || gr1_out)
					break;
				s1 = gr1->next;
				gr1_out = 1;
				continue;
			}
			if (s1->type == GRACE) {
				gr2 = s1;
				s1 = s1->extra;
				continue;
			}
			if (s1->type == BAR
			 && ((s1->sflags & S_RRBAR)
			  || s1->as.u.bar.type == B_THIN_THICK
			  || s1->as.u.bar.type == B_THICK_THIN
			  || (s1->as.u.bar.repeat_bar
			   && s1->as.text
			   && s1->as.text[0] != '1'))) {
				k = s1;
				break;
			}
			if (s1->type != NOTEREST && s1->type != SPACE) {
				s1 = s1->next;
				continue;
			}
			if (s1->as.u.note.slur_end
			 || (s1->sflags & S_SL2)) {
				k = s1;
				break;
			}
			if (s1->as.u.note.slur_st
			 || (s1->sflags & S_SL1)) {
				if (gr2) {	/* if in grace note sequence */
					for (k = s1; k->next; k = k->next)
						;
					k->next = gr2->next;
					if (gr2->next)
						gr2->next->prev = k;
//					gr2->as.u.note.slur_st = SL_AUTO;
					k = NULL;
				}
				draw_slurs(s1, last);
				if (gr2
				 && gr2->next) {
					gr2->next->prev->next = NULL;
					gr2->next->prev = gr2;
				}
			}
			if (s1 == last)
				break;
			s1 = s1->next;
		}
		if (!s1) {
			k = next_scut(s);
		} else if (!k) {
			s = s1;
			if (s == last)
				break;
			continue;
		}

		/* if slur in grace note sequence, change the linkages */
		if (gr1) {
			for (s1 = s; s1->next; s1 = s1->next)
				;
			s1->next = gr1->next;
			if (gr1->next)
				gr1->next->prev = s1;
			gr1->as.u.note.slur_st = SL_AUTO;
		}
		if (gr2) {
			gr2->prev->next = gr2->extra;
			gr2->extra->prev = gr2->prev;
			gr2->as.u.note.slur_st = SL_AUTO;
		}
		if (s->as.u.note.slur_st) {
			slur_type = s->as.u.note.slur_st & 0x07;
			s->as.u.note.slur_st >>= 3;
			m1 = -1;
		} else {
			for (m1 = 0; m1 <= s->nhd; m1++)
				if (s->as.u.note.sl1[m1])
					break;
			slur_type = s->as.u.note.sl1[m1] & 0x07;
			s->as.u.note.sl1[m1] >>= 3;
			if (s->as.u.note.sl1[m1] == 0) {
				for (i = m1 + 1; i <= s->nhd; i++)
					if (s->as.u.note.sl1[i])
						break;
				if (i > s->nhd)
					s->sflags &= ~S_SL1;
			}
		}
		m2 = -1;
		cont = 0;
		if ((k->type == NOTEREST || k->type == SPACE)
		  && (k->as.u.note.slur_end
		   || (k->sflags & S_SL2))) {
			if (k->as.u.note.slur_end) {
				k->as.u.note.slur_end--;
			} else {
				for (m2 = 0; m2 <= k->nhd; m2++)
					if (k->as.u.note.sl2[m2])
						break;
				k->as.u.note.sl2[m2]--;
				if (k->as.u.note.sl2[m2] == 0) {
					for (i = m2 + 1; i <= k->nhd; i++)
						if (k->as.u.note.sl2[i])
							break;
					if (i > k->nhd)
						k->sflags &= ~S_SL2;
				}
			}
		} else {
			if (k->type != BAR
			 || (!(k->sflags & S_RRBAR)
			  && k->as.u.bar.type != B_THIN_THICK
			  && k->as.u.bar.type != B_THICK_THIN
			  && (!k->as.u.bar.repeat_bar
			   || !k->as.text
			   || k->as.text[0] == '1')))
				cont = 1;
		}
		slur_type = draw_slur(s, k, m1, m2, slur_type);
		if (cont) {
/*fixme: the slur types are inverted*/
			voice_tb[k->voice].slur_st <<= 3;
			voice_tb[k->voice].slur_st += slur_type;
		}

		/* if slur in grace note sequence, restore the linkages */
		if (gr1
		 && gr1->next) {
			gr1->next->prev->next = NULL;
			gr1->next->prev = gr1;
		}
		if (gr2) {
			gr2->prev->next = gr2;
			gr2->extra->prev = NULL;
		}

		if (s->as.u.note.slur_st
		 || (s->sflags & S_SL1))
			continue;
		if (s == last)
			break;
		s = s->next;
	}
}

/* -- draw a tuplet -- */
/* (the staves are not yet defined) */
/* See 'tuplets' in http://moinejf.free.fr/abcm2ps-doc/index.html
 * about the value of 'u' */
static struct SYMBOL *draw_tuplet(struct SYMBOL *t,	/* tuplet in extra */
				  struct SYMBOL *s)	/* main note */
{
	struct SYMBOL *s1, *s2, *sy, *next, *g;
	int r, upstaff, nb_only, some_slur;
	float x1, x2, y1, y2, xm, ym, a, s0, yy, yx, dy;

	next = s;
	if ((t->u & 0x0f00) == 0x100)		/* if 'when' == never */
		return next;

	/* treat the nested tuplets starting on this symbol */
	for (g = t->next; g; g = g->next) {
		if (g->type == TUPLET) {
			sy = draw_tuplet(g, s);
			if (sy->time > next->time)
				next = sy;
		}
	}

	/* search the first and last notes/rests of the tuplet */
	r = t->as.u.tuplet.r_plet;
	s1 = NULL;
	some_slur = 0;
	upstaff = s->staff;
	for (s2 = s; s2; s2 = s2->next) {
		if (s2 != s) {
			for (g = s2->extra; g; g = g->next) {
				if (g->type == TUPLET) {
					sy = draw_tuplet(g, s2);
					if (sy->time > next->time)
						next = sy;
				}
			}
		}
		if (s2->type != NOTEREST) {
			if (s2->type == GRACE) {
				for (g = s2->extra; g; g = g->next) {
					if (g->type != NOTEREST)
						continue;
					if (g->as.u.note.slur_st
					 || (g->sflags & S_SL1))
						some_slur = 1;
				}
			}
			continue;
		}
		if (s2->as.u.note.slur_st	/* if slur start/end */
		 || s2->as.u.note.slur_end
		 || (s2->sflags & (S_SL1 | S_SL2)))
			some_slur = 1;
		if (s2->staff < upstaff)
			upstaff = s2->staff;
		if (!s1)
			s1 = s2;
		if (--r <= 0)
			break;
	}
	if (!s2)
		return next;			/* no solution... */
	if (s2->time > next->time)
		next = s2;

#if 0
	/* draw the slurs when inside the tuplet */
	if (some_slur) {
		draw_slurs(s1, s2);
		if (s1->as.u.note.slur_st
		 || (s1->sflags & S_SL1))
			return next;
		for (sy = s1->next; sy != s2; sy = sy->next) {
			if (sy->as.u.note.slur_st	/* if slur start/end */
			 || sy->as.u.note.slur_end
			 || (sy->sflags & (S_SL1 | S_SL2)))
				return next;		/* don't draw now */
		}
		if (s2->as.u.note.slur_end
		 || (s2->sflags & S_SL2))
			return next;
	}
#endif

	if (s1 == s2) {				/* tuplet with 1 note (!) */
		nb_only = 1;
	} else if ((t->u & 0x0f0) == 0x10) {	/* 'what' == slur */
		nb_only = 1;
		draw_slur(s1, s2, -1, -1, 
			  s1->stem > 0 ? SL_ABOVE : SL_BELOW);
	} else {

		/* search if a bracket is needed */
		if ((t->u & 0x0f00) == 0x200	/* if 'when' == always */
		 || s1->as.type != ABC_T_NOTE || s2->as.type != ABC_T_NOTE) {
			nb_only = 0;
		} else {
			nb_only = 1;
			for (sy = s1; ; sy = sy->next) {
				if (sy->type != NOTEREST) {
					if (sy->type == GRACE
					 || sy->type == SPACE)
						continue;
					nb_only = 0;
					break;
				}
				if (sy == s2)
					break;
				if (sy->sflags & S_BEAM_END) {
					nb_only = 0;
					break;
				}
			}
			if (nb_only
			 && !(s1->sflags & (S_BEAM_ST | S_BEAM_BR1 | S_BEAM_BR2))) {
				for (sy = s1->prev; sy; sy = sy->prev) {
					if (sy->type == NOTEREST) {
						if (sy->nflags >= s1->nflags)
							nb_only = 0;
						break;
					}
				}
			}
			if (nb_only && !(s2->sflags & S_BEAM_END)) {
				for (sy = s2->next; sy; sy = sy->next) {
					if (sy->type == NOTEREST) {
						if (!(sy->sflags & (S_BEAM_BR1 | S_BEAM_BR2))
						 && sy->nflags >= s2->nflags)
							nb_only = 0;
						break;
					}
				}
			}
		}
	}

	/* if number only, draw it */
	if (nb_only) {
		float a, b;

		if ((t->u & 0x0f) == 1)		/* if 'value' == none */
			return next;
		xm = (s2->x + s1->x) * .5;
		if (s1 == s2)			/* tuplet with 1 note */
			a = 0;
		else
			a = (s2->ys - s1->ys) / (s2->x - s1->x);
		b = s1->ys - a * s1->x;
		yy = a * xm + b;
		if (s1->stem > 0) {
			ym = y_get(s1->staff, 1, xm - 3, 6);
			if (ym > yy)
				b += ym - yy;
			b += 4;
		} else {
			ym = y_get(s1->staff, 0, xm - 3, 6);
			if (ym < yy)
				b += ym - yy;
			b -= 12;
		}
		for (sy = s1; ; sy = sy->next) {
			if (sy->x >= xm)
				break;
		}
		if (s1->stem * s2->stem > 0) {
			if (s1->stem > 0)
				xm += GSTEM_XOFF;
			else
				xm -= GSTEM_XOFF;
		}
		ym = a * xm + b;
		if ((t->u & 0x0f) == 0)		/* if 'value' == number */
			a2b("(%d)", t->as.u.tuplet.p_plet);
		else
			a2b("(%d:%d)", t->as.u.tuplet.p_plet,
			     t->as.u.tuplet.q_plet);
		putxy(xm, ym);
		a2b("y%d bnum\n", s1->staff);

		if (s1->stem > 0) {
			ym += 8;
			if (sy->ymx < ym)
				sy->ymx = (short) ym;
			y_set(s1->staff, 1, xm - 3, 6, ym);
		} else {
			if (sy->ymn > ym)
				sy->ymn = (short) ym;
			y_set(s1->staff, 0, xm - 3, 6, ym);
		}
		s->sflags &= ~S_IN_TUPLET;		/* the tuplet is drawn */
		return next;
	}

#if 1
	/* draw the slurs when inside the tuplet */
	if (some_slur) {
		draw_slurs(s1, s2);
		if (s1->as.u.note.slur_st
		 || (s1->sflags & S_SL1))
			return next;
		for (sy = s1->next; sy != s2; sy = sy->next) {
			if (sy->as.u.note.slur_st	/* if slur start/end */
			 || sy->as.u.note.slur_end
			 || (sy->sflags & (S_SL1 | S_SL2)))
				return next;		/* don't draw now */
		}

		/* don't draw the tuplet when a slur ends on the last note */
		if (s2->as.u.note.slur_end
		 || (s2->sflags & S_SL2))
			return next;
	}
#endif
	if ((t->u & 0x0f0) != 0)	/* if 'what' != square */
		fprintf(stderr, "'what' value of %%%%tuplets not yet coded\n");

/*fixme: two staves not treated*/
/*fixme: to optimize*/
    if (s1->multi >= 0) {

	/* sole or upper voice: the bracket is above the staff */
	x1 = s1->x - 4;
	y1 = 24;
	if (s1->staff == upstaff) {
		sy = s1;
		if (sy->as.type != ABC_T_NOTE) {
			for (sy = sy->next; sy != s2; sy = sy->next)
				if (sy->as.type == ABC_T_NOTE)
					break;
		}
		ym = y_get(upstaff, 1, sy->x, 0);
		if (ym > y1)
			y1 = ym;
		if (s1->stem > 0)
			x1 += 3;
	}
	y2 = 24;
	if (s2->staff == upstaff) {
		sy = s2;
		if (sy->as.type != ABC_T_NOTE) {
			for (sy = sy->prev; sy != s1; sy = sy->prev)
				if (sy->as.type == ABC_T_NOTE)
					break;
		}
		ym = y_get(upstaff, 1, sy->x, 0);
		if (ym > y2)
			y2 = ym;
	}

	/* end the backet according to the last note duration */
#if 1
	if (s2->dur > s2->prev->dur) {
		if (s2->next)
			x2 = s2->next->x - s2->next->wl - 5;
		else
			x2 = realwidth - 6;
	} else {
		x2 = s2->x + 4;
		r = s2->stem >= 0 ? 0 : s2->nhd;
		if (s2->shhd[r] > 0)
			x2 += s2->shhd[r];
		if (s2->staff == upstaff
		 && s2->stem > 0)
			x2 += 3.5;
	}
#else
	if (s2->next)
		x2 += (s2->next->x - s2->next->wl - s2->x - s2->wr) * 0.5;
	else
		x2 += (realwidth - s2->x) * 0.5;
#endif

	xm = .5 * (x1 + x2);
	ym = .5 * (y1 + y2);

	a = (y2 - y1) / (x2 - x1);
	s0 = 3 * (s2->pits[s2->nhd] - s1->pits[s1->nhd]) / (x2 - x1);
	if (s0 > 0) {
		if (a < 0)
			a = 0;
		else if (a > s0)
			a = s0;
	} else {
		if (a > 0)
			a = 0;
		else if (a < s0)
			a = s0;
	}
	if (a * a < .1 * .1)
		a = 0;

	/* shift up bracket if needed */
	dy = 0;
	for (sy = s1; ; sy = sy->next) {
		if (sy->dur == 0	/* not a note or a rest */
		 || sy->staff != upstaff) {
			if (sy == s2)
				break;
			continue;
		}
		yy = ym + (sy->x - xm) * a;
		yx = y_get(upstaff, 1, sy->x, 0);
		if (yx - yy > dy)
			dy = yx - yy;
		if (sy == s2)
			break;
	}

	ym += dy + 4;
	y1 = ym + a * (x1 - xm);
	y2 = ym + a * (x2 - xm);
	putxy(x2 - x1, y2 - y1);
	putxy(x1, y1 + 4);
	a2b("y%d tubr", upstaff);

	/* shift the slurs / decorations */
	ym += 8;
	for (sy = s1; ; sy = sy->next) {
		if (sy->staff == upstaff) {
			yy = ym + (sy->x - xm) * a;
			if (sy->ymx < yy)
				sy->ymx = yy;
			if (sy == s2)
				break;
			y_set(upstaff, 1, sy->x, sy->next->x - sy->x, yy);
		} else if (sy == s2) {
			break;
		}
	}

    } else {	/* lower voice of the staff: the bracket is below the staff */
/*fixme: think to all that again..*/
	x1 = s1->x - 7;

#if 1
	if (s2->dur > s2->prev->dur) {
		if (s2->next)
			x2 = s2->next->x - s2->next->wl - 8;
		else
			x2 = realwidth - 6;
	} else {
		x2 = s2->x + 2;
		if (s2->shhd[s2->nhd] > 0)
			x2 += s2->shhd[s2->nhd];
	}
#else
	if (s2->next)
		x2 += (s2->next->x - s2->next->wl - s2->x - s2->wr) * 0.5;
	else
		x2 += (realwidth - s2->x) * 0.5;
#endif

	if (s1->staff == upstaff) {
		sy = s1;
		if (sy->as.type != ABC_T_NOTE) {
			for (sy = sy->next; sy != s2; sy = sy->next)
				if (sy->as.type == ABC_T_NOTE)
					break;
		}
		y1 = y_get(upstaff, 0, sy->x, 0);
	} else {
		y1 = 0;
	}
	if (s2->staff == upstaff) {
		sy = s2;
		if (sy->as.type != ABC_T_NOTE) {
			for (sy = sy->prev; sy != s1; sy = sy->prev)
				if (sy->as.type == ABC_T_NOTE)
					break;
		}
		y2 = y_get(upstaff, 0, sy->x, 0);
	} else {
		y2 = 0;
	}

	xm = .5 * (x1 + x2);
	ym = .5 * (y1 + y2);

	a = (y2 - y1) / (x2 - x1);
	s0 = 3 * (s2->pits[0] - s1->pits[0]) / (x2 - x1);
	if (s0 > 0) {
		if (a < 0)
			a = 0;
		else if (a > s0)
			a = s0;
	} else {
		if (a > 0)
			a = 0;
		else if (a < s0)
			a = s0;
	}
	if (a * a < .1 * .1)
		a = 0;

	/* shift down bracket if needed */
	dy = 0;
	for (sy = s1; ; sy = sy->next) {
		if (sy->dur == 0	/* not a note nor a rest */
		 || sy->staff != upstaff) {
			if (sy == s2)
				break;
			continue;
		}
		yy = ym + (sy->x - xm) * a;
		yx = y_get(upstaff, 0, sy->x, 0);
		if (yx - yy < dy)
			dy = yx - yy;
		if (sy == s2)
			break;
	}

	ym += dy - 12;
	y1 = ym + a * (x1 - xm);
	y2 = ym + a * (x2 - xm);
	putxy(x2 - x1, y2 - y1);
	putxy(x1, y1 + 4);
	a2b("y%d tubrl",upstaff);

	/* shift the slurs / decorations */
	ym -= 8;
	for (sy = s1; ; sy = sy->next) {
		if (sy->staff == upstaff) {
			if (sy == s2)
				break;
			yy = ym + (sy->x - xm) * a;
			if (sy->ymn > yy)
				sy->ymn = (short) yy;
			y_set(upstaff, 0, sy->x, sy->next->x - sy->x, yy);
		}
		if (sy == s2)
			break;
	}
    } /* lower voice */

	if ((t->u & 0x0f) == 1) {	/* if 'value' == none */
		s->sflags &= ~S_IN_TUPLET;
		return next;
	}
	yy = .5 * (y1 + y2);
	if ((t->u & 0x0f) == 0)		/* if 'value' == number */
		a2b("(%d)", t->as.u.tuplet.p_plet);
	else
		a2b("(%d:%d)", t->as.u.tuplet.p_plet,
		     t->as.u.tuplet.q_plet);
	putxy(xm, yy);
	a2b("y%d bnumb\n", upstaff);
	s->sflags &= ~S_IN_TUPLET;
	return next;
}

/* -- draw the ties between two notes/chords -- */
static void draw_note_ties(struct SYMBOL *k1,
			   struct SYMBOL *k2,
			   int ntie,
			   int *mhead1,
			   int *mhead2,
			   int job)
{
	int i, s, m1, m2, p, p1, p2, y, staff;
	float x1, x2, h, sh;

	for (i = 0; i < ntie; i++) {
		m1 = mhead1[i];
		p1 = k1->pits[m1];
		m2 = mhead2[i];
		p2 = k2->pits[m2];
		s = (k1->as.u.note.ti1[m1] & 0x03) == SL_ABOVE ? 1 : -1;

		x1 = k1->x;
		sh = k1->shhd[m1];		/* head shift */
		if (s > 0) {
			if (m1 < k1->nhd && p1 + 1 == k1->pits[m1 + 1])
				if (k1->shhd[m1 + 1] > sh)
					sh = k1->shhd[m1 + 1];
		} else {
			if (m1 > 0 && p1 == k1->pits[m1 - 1] + 1)
				if (k1->shhd[m1 - 1] > sh)
					sh = k1->shhd[m1 - 1];
		}
//		x1 += sh;
		x1 += sh * 0.6;

		x2 = k2->x;
		sh = k2->shhd[m2];
		if (s > 0) {
			if (m2 < k2->nhd && p2 + 1 == k2->pits[m2 + 1])
				if (k2->shhd[m2 + 1] < sh)
					sh = k2->shhd[m2 + 1];
		} else {
			if (m2 > 0 && p2 == k2->pits[m2 - 1] + 1)
				if (k2->shhd[m2 - 1] < sh)
					sh = k2->shhd[m2 - 1];
		}
//		x2 += sh;
		x2 += sh * 0.6;

		staff = k1->staff;
		switch (job) {
		case 0:
			if (p1 == p2 || (p1 & 1))
				p = p1;
			else
				p = p2;
			break;
		case 1:				/* no starting note */
		case 3:				/* clef or staff change */
			x1 = k1->x;
			if (x1 > x2 - 20)
				x1 = x2 - 20;
			p = p2;
			staff = k2->staff;
			if (job == 3)
				s = -s;
			break;
/*		case 2:				 * no ending note */
		default:
			if (k1 != k2) {
				x2 -= k2->wl;
			} else {
				struct SYMBOL *k;

				for (k = k2->ts_next; k; k = k->ts_next)
					if (k->sflags & S_NEW_SY)
						break;
				if (!k)
					x2 = realwidth;
				else
					x2 = k->x;
			}
			if (x2 < x1 + 16)
				x2 = x1 + 16;
			p = p1;
			break;
		}
		if (x2 - x1 > 20) {
			x1 += 3.5;
			x2 -= 3.5;
		}

		y = 3 * (p - 18);
		if (job != 1 && job != 3) {
			if (p & 1)
				y += 2 * s;
			if (s > 0) {
//				if (k1->nflags > -2 && k1->stem > 0
//				 && k1->nhd == 0)
//					x1 += 4.5;
				if (!(p & 1) && k1->dots > 0)
					y = 3 * (p - 18) + 6;
			}
		} else {
			if (p & 1)
				y += 2 * s;
//			if (s < 0) {
//				if (k2->nflags > -2 && k2->stem < 0
//				 && k2->nhd == 0)
//					x2 -= 4.5;
//			}
		}

		h = (.04 * (x2 - x1) + 10) * s;
		slur_out(x1, staff_tb[staff].y + y,
			 x2, staff_tb[staff].y + y,
			 s, h, k1->as.u.note.ti1[m1] & SL_DOTTED, -1);
	}
}

/* -- draw ties between neighboring notes/chords -- */
static void draw_ties(struct SYMBOL *k1,
		      struct SYMBOL *k2,
		      int job)		/* 0: normal
					 * 1: no starting note
					 * 2: no ending note
					 * 3: no start for clef or staff change */
{
	struct SYMBOL *k3;
	int i, m1, nh1, pit, ntie, tie2, ntie3, time;
	int mhead1[MAXHD], mhead2[MAXHD], mhead3[MAXHD];

	/* special cases for ties to/from a grace note */
	if (k1->type == GRACE) {
		k3 = k1->extra;
		while (k3) {
			if (k3->type == NOTEREST)
				k1 = k3;	/* last grace note */
			k3 = k3->next;
		}
	}
	if (k2->type == GRACE) {
		k3 = k2->extra;
		while (k3) {
			if (k3->type == NOTEREST) {
				k2 = k3;	/* first grace note */
				if (job == 2)
					job = 0;
				break;
			}
			k3 = k3->next;
		}
	}

	ntie = ntie3 = 0;
	nh1 = k1->nhd;
	time = k1->time + k1->dur;

	/* half ties from last note in line or before new repeat */
	if (job == 2) {
		for (i = 0; i <= nh1; i++) {
			if (k1->as.u.note.ti1[i])
				mhead3[ntie3++] = i;
		}
		draw_note_ties(k1, k2, ntie3, mhead3, mhead3, job);
		return;
	}

	/* set up list of ties to draw */
	for (i = 0; i <= nh1; i++) {
		if (k1->as.u.note.ti1[i] == 0)
			continue;
		tie2 = -1;
		pit = k1->as.u.note.pits[i];
		for (m1 = k2->nhd; m1 >= 0; m1--) {
			switch (k2->as.u.note.pits[m1] - pit) {
			case 1:			/* maybe ^c - _d */
			case -1:		/* _d - ^c */
				if (k1->as.u.note.accs[i] != k2->as.u.note.accs[m1])
					tie2 = m1;
			default:
				continue;
			case 0:
				tie2 = m1;
				break;
			}
			break;
		}
		if (tie2 >= 0) {		/* 1st or 2nd choice */
			mhead1[ntie] = i;
			mhead2[ntie++] = tie2;
		} else {
			mhead3[ntie3++] = i;	/* no match */
		}
	}

	/* draw the ties */
	draw_note_ties(k1, k2, ntie, mhead1, mhead2, job);

	/* if any bad tie, try an other voice of the same staff */
	if (ntie3 == 0)
		return;				/* no bad tie */
	k3 = k1->ts_next;
	while (k3 && k3->time < time)
		k3 = k3->ts_next;
	while (k3 && k3->time == time) {
		if (k3->as.type != ABC_T_NOTE
		 || k3->staff != k1->staff) {
			k3 = k3->ts_next;
			continue;
		}
		ntie = 0;
		for (i = ntie3; --i >= 0; ) {
			pit = k1->as.u.note.pits[mhead3[i]];
			for (m1 = k3->nhd; m1 >= 0; m1--) {
				if (k3->as.u.note.pits[m1] == pit) {
					mhead1[ntie] = mhead3[i];
					mhead2[ntie++] = m1;
					ntie3--;
					mhead3[i] = mhead3[ntie3];
					break;
				}
			}
		}
		if (ntie > 0) {
			draw_note_ties(k1, k3,
					ntie, mhead1, mhead2,
					job == 1 ? 1 : 0);
			if (ntie3 == 0)
				return;
		}
		k3 = k3->ts_next;
	}

	if (ntie3 != 0)
		error(1, k1, "Bad tie");
}

/* -- draw all ties between neighboring notes -- */
static void draw_all_ties(struct VOICE_S *p_voice)
{
	struct SYMBOL *s1, *s2, *rtie;
	struct SYMBOL tie;
	int clef_chg;

	for (s1 = p_voice->sym; s1; s1 = s1->next) {
		switch (s1->type) {
		case CLEF:
		case KEYSIG:
		case TIMESIG:
			continue;
		}
		break;
	}
	rtie = p_voice->rtie;			/* tie from 1st repeat bar */
	for (s2 = s1; s2; s2 = s2->next) {
		if (s2->as.type == ABC_T_NOTE
		 || s2->type == GRACE)
			break;
		if (s2->type != BAR
		 || !s2->as.u.bar.repeat_bar
		 || !s2->as.text)
			continue;
		if (s2->as.text[0] == '1')	/* 1st repeat bar */
			rtie = p_voice->tie;
		else
			p_voice->tie = rtie;
	}
	if (!s2)
		return;
	if (p_voice->tie) {			/* tie from previous line */
		p_voice->tie->x = s1->x + s1->wr;
		s1 = p_voice->tie;
		p_voice->tie = NULL;
		s1->staff = s2->staff;
		s1->ts_next = s2->ts_next;	/* (for tie to other voice) */
		s1->time = s2->time - s1->dur;	/* (if after repeat sequence) */
		draw_ties(s1, s2, 1);		/* tie to 1st note */
	}

	/* search the start of ties */
	clef_chg = 0;
	for (;;) {
		for (s1 = s2; s1; s1 = s1->next) {
			if (s1->sflags & S_TI1)
				break;
			if (!rtie)
				continue;
			if (s1->type != BAR
			 || !s1->as.u.bar.repeat_bar
			 || !s1->as.text)
				continue;
			if (s1->as.text[0] == '1') {	/* 1st repeat bar */
				rtie = NULL;
				continue;
			}
			for (s2 = s1->next; s2; s2 = s2->next)
				if (s2->as.type == ABC_T_NOTE)
					break;
			if (!s2) {
				s1 = NULL;
				break;
			}
			memcpy(&tie, rtie, sizeof tie);
			tie.x = s1->x + s1->wr;
			tie.next = s2;
			tie.staff = s2->staff;
			tie.time = s2->time - tie.dur;
			draw_ties(&tie, s2, 1);
		}
		if (!s1)
			break;

		/* search the end of the tie
		 * and notice the clef changes (may occur in an other voice) */
		for (s2 = s1->ts_next; s2; s2 = s2->ts_next) {
			if (s2->staff != s1->staff
			 && s2->voice != s1->voice)
				continue;
			if (s2->type == CLEF) {
				clef_chg = 1;
				continue;
			}
			if (s2->voice != s1->voice)
				continue;
			if (s2->as.type == ABC_T_NOTE) {
				if (s2->time != s1->time + s1->dur)
					s2 = NULL;	/* %%combinevoices */
				break;
			}
			if (s2->type == GRACE)
				break;
			if (s2->type == BAR) {
				if ((s2->sflags & S_RRBAR)
				 || s2->as.u.bar.type == B_THIN_THICK
				 || s2->as.u.bar.type == B_THICK_THIN)
					break;
				if (!s2->as.u.bar.repeat_bar
				 || !s2->as.text)
					continue;
				if (s2->as.text[0] != '1')
					break;
				rtie = s1;		/* 1st repeat bar */
			}
		}
		if (!s2) {

			/* special case: tie to a combined chord */
			if (s1->ts_prev && s1->ts_prev->next) {
				struct SYMBOL *s3;
				int time;

				s3 = s1->ts_prev->next;	/* previous voice */
				time = s1->time + s1->dur;
				if (s3->time == time
				 && s3->staff == s1->staff) {
					while (s3 && s3->time == time
					    && s3->as.type != ABC_T_NOTE)
						s3 = s3->next;
					if (s3
					 && s3->time == time
					 && s3->as.type == ABC_T_NOTE) {
						draw_ties(s1, s3, 0);
						break;
					}
				}
			}
			draw_ties(s1, s1, 2);
			p_voice->tie = s1;
			break;
		}

		/* ties with clef or staff change */
		if (clef_chg || s1->staff != s2->staff) {
			float x, dx;

			clef_chg = 0;
			dx = (s2->x - s1->x) * 0.4;
			x = s2->x;
			s2->x -= dx;
			if (s2->x > s1->x + 32.)
				s2->x = s1->x + 32.;
			draw_ties(s1, s2, 2);
			s2->x = x;
			x = s1->x;
			s1->x += dx;
			if (s1->x < s2->x - 24.)
				s1->x = s2->x - 24.;
			draw_ties(s1, s2, 3);
			s1->x = x;
			continue;
		}
		draw_ties(s1, s2, s2->as.type == ABC_T_NOTE ? 0 : 2);
	}
	p_voice->rtie = rtie;
}

/* -- draw all phrasing slurs for one staff -- */
/* (the staves are not yet defined) */
static void draw_all_slurs(struct VOICE_S *p_voice)
{
	struct SYMBOL *s, *k;
	int i, m2, slur_type;
	unsigned char slur_st;

	s = p_voice->sym;
	if (!s)
		return;
	slur_type = p_voice->slur_st;
	p_voice->slur_st = 0;

	/* the starting slur types are inverted */
	slur_st = 0;
	while (slur_type != 0) {
		slur_st <<= 3;
		slur_st |= (slur_type & 0x07);
		slur_type >>= 3;
	}

	/* draw the slurs inside the music line */
	draw_slurs(s, NULL);

	/* do unbalanced slurs still left over */
	for ( ; s; s = s->next) {
		if (s->type != NOTEREST && s->type != SPACE)
			continue;
		while (s->as.u.note.slur_end
		    || (s->sflags & S_SL2)) {
			if (s->as.u.note.slur_end) {
				s->as.u.note.slur_end--;
				m2 = -1;
			} else {
				for (m2 = 0; m2 <= s->nhd; m2++)
					if (s->as.u.note.sl2[m2])
						break;
				s->as.u.note.sl2[m2]--;
				if (s->as.u.note.sl2[m2] == 0) {
					for (i = m2 + 1; i <= s->nhd; i++)
						if (s->as.u.note.sl2[i])
							break;
					if (i > s->nhd)
						s->sflags &= ~S_SL2;
				}
			}
			slur_type = slur_st & 0x07;
			k = prev_scut(s);
			draw_slur(k, s, -1, m2, slur_type);
			if (k->type != BAR
			 || (!(k->sflags & S_RRBAR)
			  && k->as.u.bar.type != B_THIN_THICK
			  && k->as.u.bar.type != B_THICK_THIN
			  && (!k->as.u.bar.repeat_bar
			   || !k->as.text
			   || k->as.text[0] == '1')))
				slur_st >>= 3;
		}
	}
	s = p_voice->sym;
	while (slur_st != 0) {
		slur_type = slur_st & 0x07;
		slur_st >>= 3;
		k = next_scut(s);
		draw_slur(s, k, -1, -1, slur_type);
		if (k->type != BAR
		 || (!(k->sflags & S_RRBAR)
		  && k->as.u.bar.type != B_THIN_THICK
		  && k->as.u.bar.type != B_THICK_THIN
		  && (!k->as.u.bar.repeat_bar
		   || !k->as.text
		   || k->as.text[0] == '1'))) {
/*fixme: the slur types are inverted again*/
			p_voice->slur_st <<= 3;
			p_voice->slur_st += slur_type;
		}
	}
}

/* -- work out accidentals to be applied to each note -- */
static void setmap(int sf,	/* number of sharps/flats in key sig (-7 to +7) */
		   unsigned char *map)	/* for 7 notes only */
{
	int j;

	for (j = 7; --j >= 0; )
		map[j] = A_NULL;
	switch (sf) {
	case 7: map[6] = A_SH;
	case 6: map[2] = A_SH;
	case 5: map[5] = A_SH;
	case 4: map[1] = A_SH;
	case 3: map[4] = A_SH;
	case 2: map[0] = A_SH;
	case 1: map[3] = A_SH;
		break;
	case -7: map[3] = A_FT;
	case -6: map[0] = A_FT;
	case -5: map[4] = A_FT;
	case -4: map[1] = A_FT;
	case -3: map[5] = A_FT;
	case -2: map[2] = A_FT;
	case -1: map[6] = A_FT;
		break;
	}
}

/* output a tablature string escaping the parenthesis */
static void tbl_out(char *s, float x, int j, char *f)
{
	char *p;

	a2b("(");
	p = s;
	for (;;) {
		while (*p != '\0' && *p != '(' && *p != ')' )
			p++;
		if (p != s) {
			a2b("%.*s", (int) (p - s), s);
			s = p;
		}
		if (*p == '\0')
			break;
		a2b("\\");
		p++;
	}
	a2b(")%.1f y %d %s ", x, j, f);
}

/* -- draw the tablature with w: -- */
static void draw_tblt_w(struct VOICE_S *p_voice,
			int nly,
			float y,
			struct tblt_s *tblt)
{
	struct SYMBOL *s;
	struct lyrics *ly;
	struct lyl *lyl;
	char *p;
	int j, l;

	a2b("/y{%.1f y%d}def ", y, p_voice->staff);
	set_font(VOCALFONT);
	a2b("%.1f 0 y %d %s\n", realwidth, nly, tblt->head);
	for (j = 0; j < nly ; j++) {
		for (s = p_voice->sym; s; s = s->next) {
			ly = s->ly;
			if (!ly
			 || (lyl = ly->lyl[j]) == NULL) {
				if (s->type == BAR) {
					if (!tblt->bar)
						continue;
					p = &tex_buf[16];
					*p-- = '\0';
					l = bar_cnv(s->as.u.bar.type);
					while (l != 0) {
						*p-- = "?|[]:???"[l & 0x07];
						l >>= 4;
					}
					p++;
					tbl_out(p, s->x, j, tblt->bar);
				}
				continue;
			}
			tbl_out(lyl->t, s->x, j, tblt->note);
		}
		a2b("\n");
	}
}

/* -- draw the tablature with automatic pitch -- */
static void draw_tblt_p(struct VOICE_S *p_voice,
			float y,
			struct tblt_s *tblt)
{
	struct SYMBOL *s;
	int j, pitch, octave, sf, tied, acc;
	unsigned char workmap[70];	/* sharps/flats - base: lowest 'C' */
	unsigned char basemap[7];
	static int scale[7] = {0, 2, 4, 5, 7, 9, 11};	/* index = natural note */
	static int acc_pitch[6] = {0, 1, 0, -1, 2, -2};	/* index = enum accidentals */

	sf = p_voice->key.sf;
	setmap(sf, basemap);
	for (j = 0; j < 10; j++)
		memcpy(&workmap[7 * j], basemap, 7);
	a2b("gsave 0 %.1f y%d T(%.2s)%s\n",
		y, p_voice->staff,
		tblt->instr, tblt->head);
	tied = 0;
	for (s = p_voice->sym; s; s = s->next) {
		switch (s->type) {
		case NOTEREST:
			if (s->as.type == ABC_T_REST)
				continue;
			if (tied) {
				tied = s->as.u.note.ti1[0];
				continue;
			}
			break;
		case KEYSIG:
			sf = s->as.u.key.sf;
			setmap(sf, basemap);
			for (j = 0; j < 10; j++)
				memcpy(&workmap[7 * j], basemap, 7);
			continue;
		case BAR:
			if (s->as.flags & ABC_F_INVIS)
				continue;
			for (j = 0; j < 10; j++)
				memcpy(&workmap[7 * j], basemap, 7);
			continue;
		default:
			continue;
		}
		pitch = s->as.u.note.pits[0] + 19;
		acc = s->as.u.note.accs[0];
		if (acc != 0) {
			workmap[pitch] = acc == A_NT
				? A_NULL
				: (acc & 0x07);
		}
		pitch = scale[pitch % 7]
			+ acc_pitch[workmap[pitch]]
			+ 12 * (pitch / 7)
			- tblt->pitch;
		octave = 0;
		while (pitch < 0) {
			pitch += 12;
			octave--;
		}
		while (pitch >= 36) {
			pitch -= 12;
			octave++;
		}
		if ((acc & 0xf8) == 0) {
			a2b("%d %d %.2f %s\n", octave, pitch, s->x, tblt->note);
		} else {
			int n, d;
			float micro_p;

			n = parse.micro_tb[acc >> 3];
			d = (n & 0xff) + 1;
			n = (n >> 8) + 1;
			switch (acc & 0x07) {
			case A_FT:
			case A_DF:
				n = -n;
				break;
			}
			micro_p = (float) pitch + (float) n / d;
			a2b("%d %.3f %.2f %s\n", octave, micro_p, s->x, tblt->note);
		}
		tied = s->as.u.note.ti1[0];
	}
	a2b("grestore\n");
}

/* -- draw the lyrics under (or above) notes -- */
/* !! this routine is tied to set_width() !! */
static float draw_lyrics(struct VOICE_S *p_voice,
			 int nly,
			 float y,
			 int incr)	/* 1: below, -1: above */
{
	int hyflag, l, j, lflag;
	char *p;
	float lastx, w, lskip, desc;
	struct SYMBOL *s;
	struct FONTSPEC *f;
	struct lyrics *ly;
	struct lyl *lyl;

	/* check if the lyrics contain tablatures */
	if (p_voice->tblts[0]) {
		if (p_voice->tblts[0]->pitch == 0)
			return y;		/* yes */
		if (p_voice->tblts[1]
		 && p_voice->tblts[1]->pitch == 0)
			return y;		/* yes */
	}

	outft = -1;				/* force font output */
	lskip = 0;				/* (compiler warning) */
	f = NULL;				/* (force new font) */
	if (incr > 0) {				/* under the staff */
		j = 0;
/*fixme: may not be the current font*/
		y -= cfmt.font_tb[VOCALFONT].size;
		if (y > -cfmt.vocalspace)
			y = -cfmt.vocalspace;
	} else {
		int top;

		top = staff_tb[p_voice->staff].topbar;
		j = nly - 1;
		nly = -1;
		if (y < top + cfmt.vocalspace - cfmt.font_tb[VOCALFONT].size)
			y = top + cfmt.vocalspace - cfmt.font_tb[VOCALFONT].size;
	}
/*fixme: may not be the current font*/
	desc = cfmt.font_tb[VOCALFONT].size * .25;	/* descent */
	for (; j != nly ; j += incr) {
		float x0, shift;

		a2b("/y{%.1f y%d}! ", y + desc, p_voice->staff);
		hyflag = lflag = 0;
		if (p_voice->hy_st & (1 << j)) {
			hyflag = 1;
			p_voice->hy_st &= ~(1 << j);
		}
		for (s = p_voice->sym; /*s*/; s = s->next)
			if (s->type != CLEF
			 && s->type != KEYSIG && s->type != TIMESIG)
				break;
		if (s->prev)
			lastx = s->prev->x;
		else
//			lastx = 0;
			lastx = tsfirst->x;
		x0 = 0;
		if (f != 0)
			lskip = f->size * 1.1;
		for ( ; s; s = s->next) {
			ly = s->ly;
			if (!ly
			 || (lyl = ly->lyl[j]) == NULL) {
				switch (s->type) {
				case NOTEREST:
					if (s->as.type == ABC_T_NOTE)
						break;
					/* fall thru */
				case MREST:
					if (lflag) {
						putx(x0 - lastx);
						putx(lastx + 3);
						a2b("y wln ");
						lflag = 0;
						lastx = s->x + s->wr;
					}
				}
				continue;
			}
			if (lyl->f != f) {		/* font change */
				f = lyl->f;
				str_font(f - cfmt.font_tb);
				if (lskip < f->size * 1.1)
					lskip = f->size * 1.1;
			}
			p = lyl->t;
			w = lyl->w;
			shift = lyl->s;
			if (hyflag) {
				if (*p == LY_UNDER) {		/* '_' */
					*p = LY_HYPH;
				} else if (*p != LY_HYPH) {	/* not '-' */
					putx(s->x - shift - lastx);
					putx(lastx);
					a2b("y hyph ");
					hyflag = 0;
					lastx = s->x + s->wr;
				}
			}
			if (lflag
			 && *p != LY_UNDER) {		/* not '_' */
				putx(x0 - lastx + 3);
				putx(lastx + 3);
				a2b("y wln ");
				lflag = 0;
				lastx = s->x + s->wr;
			}
			if (*p == LY_HYPH		/* '-' */
			 || *p == LY_UNDER) {		/* '_' */
				if (x0 == 0 && lastx > s->x - 18)
					lastx = s->x - 18;
				if (*p == LY_HYPH)
					hyflag = 1;
				else
					lflag = 1;
				x0 = s->x - shift;
				continue;
			}
			x0 = s->x - shift;
			l = strlen(p) - 1;
			if (p[l] == LY_HYPH) {		/* '-' at end */
				p[l] = '\0';
				hyflag = 1;
			}
			putx(x0);
			a2b("y M ");
			put_str(p, A_LYRIC);
			lastx = x0 + w;
		}
		if (hyflag) {
			x0 = realwidth - 10;
			if (x0 < lastx + 10)
				x0 = lastx + 10;
			putx(x0 - lastx);
			putx(lastx);
			a2b("y hyph ");
			if (cfmt.hyphencont)
				p_voice->hy_st |= (1 << j);
		}

		/* see if any underscore in the next line */
		for (s = tsnext; s; s = s->ts_next)
			if (s->voice == p_voice - voice_tb)
				break;
		for ( ; s; s = s->next) {
			if (s->as.type == ABC_T_NOTE) {
				if (s->ly && s->ly->lyl[j] != 0
				 && s->ly->lyl[j]->t[0] == LY_UNDER) {
					lflag = 1;
					x0 = realwidth - 15;
					if (x0 < lastx + 12)
						x0 = lastx + 12;
				}
				break;
			}
		}
		if (lflag) {
			putx(x0 - lastx + 3);
			putx(lastx + 3);
			a2b("y wln");
		}
		a2b("\n");
		if (incr > 0)
			y -= lskip;
		else
			y += lskip;
	}
	if (incr > 0)
		y += lskip;
	return y;
}

/* -- draw all the lyrics and the tablatures -- */
/* (the staves are not yet defined) */
static void draw_all_lyrics(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s;
	int staff, voice, nly, i;
	struct {
		short a, b;
		float top, bot;
	} lyst_tb[MAXSTAFF];
	char nly_tb[MAXVOICE];
	char above_tb[MAXVOICE];
	char rv_tb[MAXVOICE];
	float top, bot, y;

	/* check if any lyric */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (p_voice->have_ly
		 || p_voice->tblts[0])
			break;
	}
	if (!p_voice)
		return;

	/* compute the number of lyrics per voice - staff
	 * and their y offset on the staff */
	memset(above_tb, 0, sizeof above_tb);
	memset(nly_tb, 0, sizeof nly_tb);
	memset(lyst_tb, 0, sizeof lyst_tb);
	staff = -1;
	top = bot = 0;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (!p_voice->sym)
			continue;
		voice = p_voice - voice_tb;
		if (p_voice->staff != staff) {
			top = 0;
			bot = 0;
			staff = p_voice->staff;
		}
		nly = 0;
		if (p_voice->have_ly) {
			for (s = p_voice->sym; s; s = s->next) {
				struct lyrics *ly;
				float x, w;

				ly = s->ly;
				if (!ly)
					continue;
/*fixme:should get the real width*/
				x = s->x;
				if (ly->lyl[0] != 0) {
					x -= ly->lyl[0]->s;
					w = ly->lyl[0]->w;
				} else {
					w = 10;
				}
				y = y_get(p_voice->staff, 1, x, w);
				if (top < y)
					top = y;
				y = y_get(p_voice->staff, 0, x, w);
				if (bot > y)
					bot = y;
				for (i = MAXLY; --i >= 0; )
					if (ly->lyl[i] != 0)
						break;
				i++;
				if (i > nly)
					nly = i;
			}
		} else {
			y = y_get(p_voice->staff, 1, 0, realwidth);
			if (top < y)
				top = y;
			y = y_get(p_voice->staff, 0, 0, realwidth);
			if (bot > y)
				bot = y;
		}
		lyst_tb[staff].top = top;
		lyst_tb[staff].bot = bot;
		if (nly == 0)
			continue;
		nly_tb[voice] = nly;
		if (p_voice->posit.voc != 0)
			above_tb[voice] = p_voice->posit.voc == SL_ABOVE;
		else if (p_voice->next
/*fixme:%%staves:KO - find an other way..*/
		      && p_voice->next->staff == staff
		      && p_voice->next->have_ly)
			above_tb[voice] = 1;
		if (above_tb[voice])
			lyst_tb[staff].a = 1;
		else
			lyst_tb[staff].b = 1;
	}

	/* draw the lyrics under the staves */
	i = 0;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		struct tblt_s *tblt;

		if (!p_voice->sym)
			continue;
		if (!p_voice->have_ly
		 && !p_voice->tblts[0])
			continue;
		voice = p_voice - voice_tb;
		if (above_tb[voice]) {
			rv_tb[i++] = voice;
			continue;
		}
		staff = p_voice->staff;
		set_sscale(staff);
		if (nly_tb[voice] > 0)
			lyst_tb[staff].bot = draw_lyrics(p_voice, nly_tb[voice],
							 lyst_tb[staff].bot, 1);
		for (nly = 0; nly < 2; nly++) {
			if ((tblt = p_voice->tblts[nly]) == NULL)
				break;
			if (tblt->hu > 0) {
				lyst_tb[staff].bot -= tblt->hu;
				lyst_tb[staff].b = 1;
			}
			if (tblt->pitch == 0)
				draw_tblt_w(p_voice, nly_tb[voice],
					lyst_tb[staff].bot, tblt);
			else
				draw_tblt_p(p_voice, lyst_tb[staff].bot,
					tblt);
			if (tblt->ha != 0) {
				lyst_tb[staff].top += tblt->ha;
				lyst_tb[staff].a = 1;
			}
		}
	}

	/* draw the lyrics above the staff */
	while (--i >= 0) {
		voice = rv_tb[i];
		p_voice = &voice_tb[voice];
		staff = p_voice->staff;
		set_sscale(staff);
		lyst_tb[staff].top = draw_lyrics(p_voice, nly_tb[voice],
						 lyst_tb[staff].top, -1);
	}

	/* set the max y offsets of all symbols */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (!p_voice->sym)
			continue;
		staff = p_voice->staff;
		set_sscale(staff);
		if (lyst_tb[staff].a) {
			top = lyst_tb[staff].top + 2;
			for (s = p_voice->sym; s; s = s->next) {
/*fixme: may have lyrics crossing a next symbol*/
				if (s->ly) {
/*fixme:should set the real width*/
					y_set(staff, 1, s->x - 2, 10, top);
				}
			}
		}
		if (lyst_tb[staff].b) {
			bot = lyst_tb[staff].bot - 2;
			if (nly_tb[p_voice - voice_tb] > 0) {
				for (s = p_voice->sym; s; s = s->next) {
					if (s->ly) {
/*fixme:should set the real width*/
						y_set(staff, 0, s->x - 2, 10, bot);
					}
				}
			} else {
				y_set(staff, 0, 0, realwidth, bot);
			}
		}
	}
}

/* -- draw the symbols near the notes -- */
/* (the staves are not yet defined) */
/* order:
 * - beams
 * - decorations near the notes
 * - measure bar numbers
 * - n-plets
 * - decorations tied to the notes
 * - slurs
 * - guitar chords
 * - then remaining decorations
 * The buffer output is delayed until the definition of the staff system,
 * so, global variables must be saved (see music.c delayed_output()).
 */
void draw_sym_near(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s;

	/* calculate the beams but don't draw them (the staves are not yet defined) */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		struct BEAM bm;
		int first_note = 1;

		for (s = p_voice->sym; s; s = s->next) {
			if (s->as.type != ABC_T_NOTE)
				continue;
			if (((s->sflags & S_BEAM_ST) && !(s->sflags & S_BEAM_END))
			 || (first_note && !(s->sflags & S_BEAM_ST))) {
				first_note = 0;
				calculate_beam(&bm, s);
			}
		}
	}

	/* initialize the y offsets */
	{
		int i, staff;

		for (staff = 0; staff <= nstaff; staff++) {
			for (i = 0; i < YSTEP; i++) {
				staff_tb[staff].top[i] = 0;
				staff_tb[staff].bot[i] = 24;
			}
		}
	}

	set_tie_room();
	draw_deco_near();

	/* set the min/max vertical offsets */
	for (s = tsfirst; s; s = s->ts_next) {
		int y;
		struct SYMBOL *g;

//		if (!s->prev)
//			continue;		/* skip the clefs */
		if (s->as.flags & ABC_F_INVIS)
			continue;
		if (s->type == GRACE) {
			float w = ((cfmt.gracespace >> 8) & 0xff) * 0.1;
			g = s->extra;
			for ( ; g; g = g->next) {
				y_set(s->staff, 1, g->x - w / 2, w,
						g->ymx + 1);
				y_set(s->staff, 0, g->x - w / 2, w,
						g->ymn - 1);
			}
			continue;
		}
		if (s->type != MREST) {
			y_set(s->staff, 1, s->x - s->wl, s->wl + s->wr, s->ymx + 2);
			y_set(s->staff, 0, s->x - s->wl, s->wl + s->wr, s->ymn - 2);
		} else {
			y_set(s->staff, 1, s->x - 16, 32, s->ymx + 2);
		}
		if (s->as.type != ABC_T_NOTE)
			continue;

		/* have room for the accidentals */
		if (s->as.u.note.accs[s->nhd]) {
			y = s->y + 8;
			if (s->ymx < y)
				s->ymx = y;
			y_set(s->staff, 1, s->x, 0., y);
		}
		if (s->as.u.note.accs[0]) {
			y = s->y;
			if ((s->as.u.note.accs[0] & 0x07) == A_SH
			 || s->as.u.note.accs[0] == A_NT)
				y -= 7;
			else
				y -= 5;
			if (s->ymn > y)
				s->ymn = y;
			y_set(s->staff, 0, s->x, 0., y);
		}
	}

	if (cfmt.measurenb >= 0)
		draw_measnb();

	draw_deco_note();

	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		s = p_voice->sym;
		if (!s)
			continue;
//fixme: KO: the voice has no color yet
//		set_v_color(s.voice);
		set_sscale(cursys->voice[p_voice - voice_tb].staff);

		/* draw the tuplets near the notes */
		for (s = s->next; s; s = s->next) {
			struct SYMBOL *g;

			if ((s->sflags & S_IN_TUPLET)
			 && (g = s->extra) != NULL) {
				for ( ; g; g = g->next) {
					if (g->type == TUPLET) {
						s = draw_tuplet(g, s);
						break;
					}
				}
			}
		}
		draw_all_slurs(p_voice);

		/* draw the tuplets over the slurs */
		for (s = p_voice->sym; s; s = s->next) {
			struct SYMBOL *g;

			if ((s->sflags & S_IN_TUPLET)
			 && (g = s->extra) != NULL) {
				for ( ; g; g = g->next) {
					if (g->type == TUPLET) {
						s = draw_tuplet(g, s);
						break;
					}
				}
			}
		}
	}

	/* set the top and bottom for all symbols to be out of the staves */
	{
		int top, bot, i, staff;

		for (staff = 0; staff <= nstaff; staff++) {
			top = staff_tb[staff].topbar + 2;
			bot = staff_tb[staff].botbar - 2;
			for (i = 0; i < YSTEP; i++) {
				if (top > staff_tb[staff].top[i])
					staff_tb[staff].top[i] = (float) top;
				if (bot < staff_tb[staff].bot[i])
					staff_tb[staff].bot[i] = (float) bot;
			}
		}
	}
	draw_all_lyrics();
	draw_deco_staff();
	set_sscale(-1);		/* restore the scale parameters */
}

/* -- draw the name/subname of the voices -- */
static void draw_vname(float indent)
{
	struct VOICE_S *p_voice;
	int n, staff;
	struct {
		int nl;
		char *v[8];
	} staff_d[MAXSTAFF], *staff_p;
	char *p, *q;
	float y;

	for (staff = cursys->nstaff; staff >= 0; staff--) {
		if (!cursys->staff[staff].empty)
			break;
	}
	if (staff < 0)
		return;

	memset(staff_d, 0, sizeof staff_d);
	n = 0;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (!p_voice->sym)
			continue;
		staff = cursys->voice[p_voice - voice_tb].staff;
		if (cursys->staff[staff].empty)
			continue;
		if (p_voice->new_name) {
			p_voice->new_name = 0;
			p = p_voice->nm;
		} else {
			p = p_voice->snm;
		}
		if (!p)
			continue;
		if (cursys->staff[staff].flags & CLOSE_BRACE2) {
			while (!(cursys->staff[staff].flags & OPEN_BRACE2))
				staff--;
		} else if (cursys->staff[staff].flags & CLOSE_BRACE) {
			while (!(cursys->staff[staff].flags & OPEN_BRACE))
				staff--;
		}
		staff_p = &staff_d[staff];
		for (;;) {
			staff_p->v[staff_p->nl++] = p;
			p = strstr(p, "\\n");
			if (!p
			 || staff_p->nl >= MAXSTAFF)
				break;
			p += 2;
		}
		n++;
	}
	if (n == 0)
		return;
	str_font(VOICEFONT);
	indent = -indent * .5;			/* center */
	for (staff = nstaff; staff >= 0; staff--) {
		staff_p = &staff_d[staff];
		if (staff_p->nl == 0)
			continue;
		y = staff_tb[staff].y
			+ staff_tb[staff].topbar * .5
				* staff_tb[staff].staffscale
			+ 9 * (staff_p->nl - 1)
			- cfmt.font_tb[VOICEFONT].size * .3;
		n = staff;
		if (cursys->staff[staff].flags & OPEN_BRACE2) {
			while (!(cursys->staff[n].flags & CLOSE_BRACE2))
				n++;
		} else if (cursys->staff[staff].flags & OPEN_BRACE) {
			while (!(cursys->staff[n].flags & CLOSE_BRACE))
				n++;
		}
		if (n != staff)
			y -= (staff_tb[staff].y - staff_tb[n].y) * .5;
		for (n = 0; n < staff_p->nl; n++) {
			p = staff_p->v[n];
			q = strstr(p, "\\n");
			if (q)
				*q = '\0';
			a2b("%.1f %.1f M ", indent, y);
			put_str(p, A_CENTER);
			y -= 18.;
			if (q)
				*q = '\\';
		}
	}
}

/* -- set the y offset of the staves and return the whole height -- */
static float set_staff(void)
{
	struct SYSTEM *sy;
	struct SYMBOL *s;
	int i, staff, prev_staff;
	float y, staffsep, dy, maxsep, mbot, v;
	char empty[MAXSTAFF];

	/* search the empty staves in each parts */
	memset(empty, 1, sizeof empty);
	for (staff = 0; staff <= nstaff; staff++)
		staff_tb[staff].empty = 0;
	sy = cursys;
	for (staff = 0; staff <= nstaff; staff++) {
		if (!sy->staff[staff].empty)
			empty[staff] = 0;
	}
	for (s = tsfirst; s; s = s->ts_next) {
		if (!(s->sflags & S_NEW_SY))
			continue;
		sy = sy->next;
		for (staff = 0; staff <= nstaff; staff++) {
			if (!sy->staff[staff].empty)
				empty[staff] = 0;
		}
	}

	/* output the scale of the voices
	 * and flag as non empty the staves with tablatures */
	{
		struct VOICE_S *p_voice;

		for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
			if (p_voice->scale != 1)
				a2b("/scvo%d{gsave %.2f dup scale}!\n",
				     (int) (p_voice - voice_tb),
				     p_voice->scale);
			if (p_voice->tblts[0])
				empty[p_voice->staff] = 0;
		}
	}

	/* set the vertical offset of the 1st staff */
	for (staff = 0; staff <= nstaff; staff++) {
		if (!empty[staff])
			break;
		staff_tb[staff].empty = 1;
	}
	y = 0;
	if (staff > nstaff) {
		staff--;			/* one staff, empty */
	} else {
		for (i = 0; i < YSTEP; i++) {
			v = staff_tb[staff].top[i];
			if (y < v)
				y = v;
		}
	}

	/* draw the parts and tempo indications if any */
	y += draw_partempo(staff, y);

	if (empty[staff])
		return y;

	staffsep = cfmt.staffsep * 0.5 +
			staff_tb[staff].topbar * staff_tb[staff].staffscale;
	if (y < staffsep)
		y = staffsep;
	staff_tb[staff].y = -y;

	/* set the offset of the other staves */
	prev_staff = staff;
	for (staff++; staff <= nstaff; staff++) {
		if (empty[staff]) {
			staff_tb[staff].empty = 1;
			continue;
		}
		if (sy->staff[prev_staff].sep != 0)
			staffsep = sy->staff[prev_staff].sep;
		else
			staffsep = cfmt.sysstaffsep;
		if (sy->staff[prev_staff].maxsep != 0)
			maxsep = sy->staff[prev_staff].maxsep;
		else
			maxsep = cfmt.maxsysstaffsep;

		dy = 0;
		if (staff_tb[staff].staffscale
				== staff_tb[prev_staff].staffscale) {
			for (i = 0; i < YSTEP; i++) {
				v = staff_tb[staff].top[i]
				  - staff_tb[prev_staff].bot[i];
				if (dy < v)
					dy = v;
			}
			dy *= staff_tb[staff].staffscale;
		} else {
			for (i = 0; i < YSTEP; i++) {
				v = staff_tb[staff].top[i]
					* staff_tb[staff].staffscale
				  - staff_tb[prev_staff].bot[i]
					* staff_tb[prev_staff].staffscale;
				if (dy < v)
					dy = v;
			}
		}
		staffsep += staff_tb[staff].topbar
				* staff_tb[staff].staffscale;
		if (dy < staffsep)
			dy = staffsep;
		maxsep += staff_tb[staff].topbar
				* staff_tb[staff].staffscale;
		if (dy > maxsep)
			dy = maxsep;
		y += dy;
		staff_tb[staff].y = -y;

		prev_staff = staff;
	}
	mbot = 0;
	for (i = 0; i < YSTEP; i++) {
		v = staff_tb[prev_staff].bot[i];
		if (mbot > v)
			mbot = v;
	}
	mbot *= staff_tb[prev_staff].staffscale;

	/* output the staff offsets */
	for (staff = nstaff; staff >= 0; staff--) {
		dy = staff_tb[staff].y;
		if (staff_tb[staff].staffscale != 1
		 && staff_tb[staff].staffscale != 0) {
			a2b("/scst%d{gsave 0 %.2f T %.2f dup scale}!\n",
			     staff, dy, staff_tb[staff].staffscale);
			a2b("/y%d{}!\n", staff);
		} else {
			a2b("/y%d{%.1f add}!\n", staff, dy);
		}
	}

	if (mbot == 0) {
		for (staff = nstaff; staff >= 0; staff--) {
			if (!empty[staff])
				break;
		}
		if (staff < 0)		/* no symbol in this system ! */
			return y;
	}
	dy = -mbot;
	staffsep = cfmt.staffsep * 0.5;
	if (dy < staffsep)
		dy = staffsep;
	maxsep = cfmt.maxstaffsep * 0.5;
	if (dy > maxsep)
		dy = maxsep;
	y += dy;
	if (y > cfmt.maxstaffsep)
		y = cfmt.maxstaffsep;

	/* return the whole staff system height */
	return y;
}

/* -- set the bottom and height of the measure bars -- */
static void bar_set(float *bar_bot, float *bar_height)
{
	int staff, nlines;
	float dy, staffscale;
			/* !! max number of staff lines !! */
	static const char top[10] = {18, 18, 12, 18, 18, 24, 30, 36, 42, 48};
	static const char bot[10] = { 6,  6,  6,  6,  0,  0,  0,  0,  0,  0};

	dy = 0;
	for (staff = 0; staff <= nstaff; staff++) {
		nlines = staff_tb[staff].stafflines;
		staffscale = staff_tb[staff].staffscale;
		if (cursys->staff[staff].empty) {
			bar_bot[staff] = bar_height[staff] = 0;
			continue;
		}
		if (dy == 0)
			dy = staff_tb[staff].y + top[nlines] * staffscale;
		bar_height[staff] = dy
				- staff_tb[staff].y - bot[nlines] * staffscale;

		bar_bot[staff] = staff_tb[staff].y + bot[nlines] * staffscale;

		if (cursys->staff[staff].flags & STOP_BAR)
			dy = 0;
		else
			dy = bar_bot[staff];
	}
}

/* -- draw the staff systems and the measure bars -- */
float draw_systems(float indent)
{
	struct SYSTEM *next_sy;
	struct SYMBOL *s, *s2;
	int staff;
	float xstaff[MAXSTAFF], bar_bot[MAXSTAFF], bar_height[MAXSTAFF];
	float x, x2;
	float line_height;

	line_height = set_staff();
	draw_vname(indent);

	/* draw the staff, skipping the staff breaks */
	for (staff = 0; staff <= nstaff; staff++)
		xstaff[staff] = cursys->staff[staff].empty ? -1 : 0;
	bar_set(bar_bot, bar_height);
	draw_lstaff(0);
	for (s = tsfirst; s; s = s->ts_next) {
		if (s->sflags & S_NEW_SY) {
			next_sy = cursys->next;
			for (staff = 0; staff <= nstaff; staff++) {
//				if (next_sy->staff[staff].empty
//						== cursys->staff[staff].empty
//				 && next_sy->staff[staff].stafflines
//						== cursys->staff[staff].stafflines)
//					continue;
				x = xstaff[staff];
				if (x < 0) {			// no staff yet
					if (!next_sy->staff[staff].empty) {
						if (s->type == BAR)
							xstaff[staff] = s->x;
						else
							xstaff[staff] = s->x - s->wl - 2;
					}
					continue;
				}
				if (s->ts_prev->type == BAR)
					x2 = s->ts_prev->x;
				else
					x2 = s->x - s->wl - 2;
				if (next_sy->staff[staff].empty) { // staff stop
					xstaff[staff] = -1;
				} else if (next_sy->staff[staff].stafflines
						== cursys->staff[staff].stafflines) {
					continue;
				} else {
					xstaff[staff] = x2;
				}
				draw_staff(staff, x, x2);
			}
			cursys = next_sy;
			bar_set(bar_bot, bar_height);
		}
		staff = s->staff;
		switch (s->type) {
		case BAR:
			if ((s->sflags & S_SECOND)
			 || cursys->staff[staff].empty)
				s->as.flags |= ABC_F_INVIS;
			if (s->as.flags & ABC_F_INVIS)
				break;
			draw_bar(s, bar_bot[staff], bar_height[staff]);
			if (annotate)
				anno_out(s, 'B');
			break;
		case STBRK:
			if (cursys->voice[s->voice].range == 0) {
				if (s->xmx > .5 CM) {
					int i, nvoice;

					/* draw the left system if stbrk in all voices */
					nvoice = 0;
					for (i = 0; i < MAXVOICE; i++) {
						if (cursys->voice[i].range > 0)
							nvoice++;
					}
					for (s2 = s->ts_next; s2; s2 = s2->ts_next) {
						if (s2->type != STBRK)
							break;
						nvoice--;
					}
					if (nvoice == 0)
						draw_lstaff(s->x);
				}
			}
			s2 = s->prev;
			if (!s2)
				break;
			x2 = s2->x;
			if (s2->type != BAR)
				x2 += s2->wr;
			staff = s->staff;
			x = xstaff[staff];
			if (x >= 0) {
				if (x >= x2)
					continue;
				draw_staff(staff, x, x2);
			}
			xstaff[staff] = s->x;
			break;
		default:
//fixme:does not work for "%%staves K: M: $" */
//removed for K:/M: in empty staves
//			if (cursys->staff[staff].empty)
//				s->as.flags |= ABC_F_INVIS;
			break;
		}
	}
	for (staff = 0; staff <= nstaff; staff++) {
		if ((x = xstaff[staff]) < 0)
//		 || x >= realwidth - 8)
			continue;
		draw_staff(staff, x, realwidth);
	}
	set_sscale(-1);
	return line_height;
}

/* -- output PostScript sequences and set the staff and voice colors -- */
void output_ps(struct SYMBOL *s, int color)
{
	struct SYMBOL *g, *g2;

	g = s->extra;
	g2 = NULL;
	for ( ; g; g = g->next) {
		if (g->type == FMTCHG) {
			switch (g->u) {
			case PSSEQ:
			case SVGSEQ:
				if (g->u == SVGSEQ)
					a2b("%%svg %s\n", g->as.text);
				else
					a2b("%s\n", g->as.text);
				if (!g2)
					s->extra = g->next;
				else
					g2->next = g->next;
				continue;
			case STAFF_COLOR:
				staff_tb[s->staff].color =
					g->as.u.length.base_length;
				if (color)
					set_st_color(s->staff);
				break;
			case VOICE_COLOR:
				voice_tb[s->voice].color =
					g->as.u.length.base_length;
				if (color)
					set_v_color(s->voice);
				break;
			}
		}
		g2 = g;
	}
}

/* -- draw remaining symbols when the staves are defined -- */
static void draw_symbols(struct VOICE_S *p_voice)
{
	struct BEAM bm;
	struct SYMBOL *s;
	float x, y;
	int staff, first_note;

#if 0
	/* output the PostScript code at start of line */
	for (s = p_voice->sym; s; s = s->next) {
		if (s->extra)
			output_ps(s, 1);
		switch (s->type) {
		case CLEF:
		case KEYSIG:
		case TIMESIG:
		case BAR:
			continue;	/* skip the symbols added by init_music_line() */
		}
		break;
	}
#endif

	bm.s2 = NULL;
	first_note = 1;
	set_v_color(p_voice - voice_tb);
	for (s = p_voice->sym; s; s = s->next) {
		if (s->extra)
			output_ps(s, 1);
		if ((s->as.flags & ABC_F_INVIS)
		 && s->type != NOTEREST && s->type != GRACE)
			continue;
		x = s->x;
		switch (s->type) {
		case NOTEREST:
			set_scale(s);
			if (s->as.type == ABC_T_NOTE) {
				if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST
				 || (first_note && !(s->sflags & S_BEAM_ST))) {
					first_note = 0;
					if (calculate_beam(&bm, s)) {
						if (annotate)
							anno_out(s, 'b');
						draw_beams(&bm);
					}
				}
				draw_note(x, s, bm.s2 == NULL);
				if (annotate)
					anno_out(s, 'N');
				if (s == bm.s2)
					bm.s2 = NULL;
				if (annotate
				 && (s->sflags & (S_BEAM_ST | S_BEAM_END))
							== S_BEAM_END)
					anno_out(s, 'e');
				break;
			}
			draw_rest(s);
			if (annotate)
				anno_out(s, 'R');
			break;
		case BAR:
			break;			/* drawn in draw_systems */
		case CLEF:
//fixme: set staff color
			staff = s->staff;
			if (s->sflags & S_SECOND)
/*			 || p_voice->staff != staff)	*/
				break;		/* only one clef per staff */
			if ((s->as.flags & ABC_F_INVIS)
			 || staff_tb[staff].empty)
				break;
			set_sscale(staff);
			y = staff_tb[staff].y;
			x -= 10;		/* clef shift - see set_width() */
			putxy(x, y + s->y);
			if (s->as.u.clef.name)
				a2b("%s\n", s->as.u.clef.name);
			else
				a2b("%c%cclef\n",
				     s->u ? 's' : ' ',
				     "tcbp"[(unsigned) s->as.u.clef.type]);
			if (s->as.u.clef.octave != 0) {
/*fixme:break the compatibility and avoid strange numbers*/
				if (s->as.u.clef.octave > 0)
					y += s->ymx - 12;
				else
					y += s->ymn + 2;
				putxy(x, y);
				a2b("oct%c\n",
				     s->as.u.clef.octave > 0 ? 'u' : 'l');
			}
			if (annotate)
				anno_out(s, 'c');
			break;
		case TIMESIG:
//fixme: set staff color
			memcpy(&p_voice->meter, &s->as.u.meter,
					sizeof p_voice->meter);
			if ((s->sflags & S_SECOND)
			 || staff_tb[s->staff].empty)
				break;
			if (cfmt.alignbars && s->staff != 0)
				break;
			set_sscale(s->staff);
			draw_timesig(x, s);
			if (annotate)
				anno_out(s, 'M');
			break;
		case KEYSIG:
//fixme: set staff color
			memcpy(&p_voice->key, &s->as.u.key,
					sizeof p_voice->key);
			if ((s->sflags & S_SECOND)
			 || staff_tb[s->staff].empty)
				break;
			set_sscale(s->staff);
			draw_keysig(p_voice, x, s);
			if (annotate)
				anno_out(s, 'K');
			break;
		case MREST:
			set_scale(s);
			a2b("(%d)", s->as.u.bar.len);
			putxy(x, staff_tb[s->staff].y);
			a2b("mrest\n");
			break;
		case GRACE:
			set_scale(s);
			draw_gracenotes(s);
			break;
		case SPACE:
		case STBRK:
		case FMTCHG:
			break;			/* nothing */
		case CUSTOS:
			set_scale(s);
			s->sflags |= ABC_F_STEMLESS;
			draw_note(x, s, 0);
			break;
		default:
			bug("Symbol not drawn", 1);
		}
	}
	set_scale(p_voice->sym);
	draw_all_ties(p_voice);
	set_v_color(-1);
}

/* -- draw all symbols -- */
void draw_all_symb(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s;

	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
#if 0 /* no: did display the rests when "%%staffnonote 0" */
		if (!p_voice->sym)
#else
		if (!p_voice->sym
		 || staff_tb[p_voice->staff].empty)
#endif
			continue;
		draw_symbols(p_voice);
	}

	// update the clefs
	for (s = tsfirst; s; s = s->ts_next) {
		if (s->type == CLEF)
			staff_tb[s->staff].s_clef = s;
	}
}

/* -- output a floating value, and x and y according to the current scale -- */
void putf(float v)
{
	a2b("%.1f ", v);
}

void putx(float x)
{
	putf(x / cur_scale);
}

void puty(float y)
{
	putf(scale_voice ?
		y / cur_scale :		/* scaled voice */
		y - cur_trans);		/* scaled staff */
}

void putxy(float x, float y)
{
	if (scale_voice)
		a2b("%.1f %.1f ",
		     x / cur_scale, y / cur_scale);	/* scaled voice */
	else
		a2b("%.1f %.1f ",
		     x / cur_scale, y - cur_trans);	/* scaled staff */
}

/* -- set the voice or staff scale -- */
void set_scale(struct SYMBOL *s)
{
	float scale, trans;

	scale = voice_tb[s->voice].scale;
	if (scale == 1) {
		set_sscale(s->staff);
		return;
	}
/*fixme: KO when both staff and voice are scaled */
	trans = 0;
	scale_voice = 1;
	if (scale == cur_scale && trans == cur_trans)
		return;
	if (cur_scale != 1)
		a2b("grestore ");
	cur_scale = scale;
	cur_trans = trans;
	if (scale != 1)
		a2b("scvo%d ", s->voice);
}

/* -- set the staff scale (only) -- */
void set_sscale(int staff)
{
	float scale, trans;

	scale_voice = 0;
	if (staff != cur_staff && cur_scale != 1)
		cur_scale = 0;
	if (staff >= 0)
		scale = staff_tb[staff].staffscale;
	else
		scale = 1;
	if (staff >= 0 && scale != 1)
		trans = staff_tb[staff].y;
	else
		trans = 0;
	if (scale == cur_scale && trans == cur_trans)
		return;
	if (cur_scale != 1)
		a2b("grestore ");
	cur_scale = scale;
	cur_trans = trans;
	if (scale != 1) {
		a2b("scst%d ", staff);
		cur_staff = staff;
	}
}

/* -- set the tie directions for one voice -- */
static void set_tie_dir(struct SYMBOL *sym)
{
	struct SYMBOL *s;
	int i, ntie, dir, sec, pit, ti;

	for (s = sym; s; s = s->next) {
		if (!(s->sflags & S_TI1))
			continue;

		/* if other voice, set the ties in opposite direction */
		if (s->multi != 0) {
/*			struct SYMBOL *s2;

			s2 = s->ts_next;
			if (s2->time == s->time && s2->staff == s->staff) { */
				dir = s->multi > 0 ? SL_ABOVE : SL_BELOW;
				for (i = 0; i <= s->nhd; i++) {
					ti = s->as.u.note.ti1[i];
					if (!((ti & 0x03) == SL_AUTO))
						continue;
					s->as.u.note.ti1[i] = (ti & SL_DOTTED) | dir;
				}
				continue;
/*			} */
		}

		/* if one note, set the direction according to the stem */
		sec = ntie = 0;
		pit = 128;
		for (i = 0; i <= s->nhd; i++) {
			if (s->as.u.note.ti1[i]) {
				ntie++;
				if (pit < 128
				 && s->as.u.note.pits[i] <= pit + 1)
					sec++;
				pit = s->as.u.note.pits[i];
			}
		}
		if (ntie <= 1) {
			dir = s->stem < 0 ? SL_ABOVE : SL_BELOW;
			for (i = 0; i <= s->nhd; i++) {
				ti = s->as.u.note.ti1[i];
				if (ti != 0) {
					if ((ti & 0x03) == SL_AUTO)
						s->as.u.note.ti1[i] =
							(ti & SL_DOTTED) | dir;
					break;
				}
			}
			continue;
		}
		if (sec == 0) {
			if (ntie & 1) {
/* in chords with an odd number of notes, the outer noteheads are paired off
 * center notes are tied according to their position in relation to the
 * center line */
				ntie = ntie / 2;
				dir = SL_BELOW;
				for (i = 0; i <= s->nhd; i++) {
					ti = s->as.u.note.ti1[i];
					if (ti == 0)
						continue;
					if (ntie == 0) {	/* central tie */
						if (s->pits[i] >= 22)
							dir = SL_ABOVE;
					}
					if ((ti & 0x03) == SL_AUTO)
						s->as.u.note.ti1[i] =
							(ti & SL_DOTTED) | dir;
					if (ntie-- == 0)
						dir = SL_ABOVE;
				}
				continue;
			}
/* even number of notes, ties divided in opposite directions */
			ntie /= 2;
			dir = SL_BELOW;
			for (i = 0; i <= s->nhd; i++) {
				ti = s->as.u.note.ti1[i];
				if (ti == 0)
					continue;
				if ((ti & 0x03) == SL_AUTO)
					s->as.u.note.ti1[i] =
						(ti & SL_DOTTED) | dir;
				if (--ntie == 0)
					dir = SL_ABOVE;
			}
			continue;
		}
/*fixme: treat more than one second */
/*		if (nsec == 1) {	*/
/* When a chord contains the interval of a second, tie those two notes in
 * opposition; then fill in the remaining notes of the chord accordingly */
			pit = 128;
			for (i = 0; i <= s->nhd; i++) {
				if (s->as.u.note.ti1[i]) {
					if (pit < 128
					 && s->as.u.note.pits[i] <= pit + 1) {
						ntie = i;
						break;
					}
					pit = s->as.u.note.pits[i];
				}
			}
			dir = SL_BELOW;
			for (i = 0; i <= s->nhd; i++) {
				ti = s->as.u.note.ti1[i];
				if (ti == 0)
					continue;
				if (ntie == i)
					dir = SL_ABOVE;
				if ((ti & 0x03) == SL_AUTO)
					s->as.u.note.ti1[i] =
							(ti & SL_DOTTED) | dir;
			}
/*fixme..
			continue;
		}
..*/
/* if a chord contains more than one pair of seconds, the pair farthest
 * from the center line receives the ties drawn in opposition */
	}
}

/* -- have room for the ties out of the staves -- */
static void set_tie_room(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s, *s2;

	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		s = p_voice->sym;
		if (!s)
			continue;
		s = s->next;
		if (!s)
			continue;
		set_tie_dir(s);
		for ( ; s; s = s->next) {
			float dx, y, dy;

			if (!(s->sflags & S_TI1))
				continue;
			if (s->pits[0] < 20 && s->as.u.note.ti1[0] == SL_BELOW)
				;
			else if (s->pits[s->nhd] > 24
			      && s->as.u.note.ti1[s->nhd] == SL_ABOVE)
				;
			else
				continue;
			s2 = s->next;
			while (s2 && s2->as.type != ABC_T_NOTE)
				s2 = s2->next;
			if (s2) {
				if (s2->staff != s->staff)
					continue;
				dx = s2->x - s->x - 10;
			} else {
				dx = realwidth - s->x - 10;
			}
			if (dx < 100)
				dy = 9;
			else if (dx < 300)
				dy = 12;
			else
				dy = 16;
			if (s->pits[s->nhd] > 24) {
				y = 3 * (s->pits[s->nhd] - 18) + dy;
				if (s->ymx < y)
					s->ymx = y;
				if (s2 && s2->ymx < y)
					s2->ymx = y;
				y_set(s->staff, 1, s->x + 5, dx, y);
			}
			if (s->pits[0] < 20) {
				y = 3 * (s->pits[0] - 18) - dy;
				if (s->ymn > y)
					s->ymn = y;
				if (s2 && s2->ymn > y)
					s2->ymn = y;
				y_set(s->staff, 0, s->x + 5, dx, y);
			}
		}
	}
}
abcm2ps-8.5.2/flute.fmt0000644000175000017500000002032312055357464013011 0ustar  jefjef% format file for flute tablatures - this file is part of abcm2ps
%
% Tin whistle by Guido Gonzato
% Galoubet and Pipe-Tabor by Michel Bellon
%
% tw_   : tin whistle
% gbl_  : galoubet (3 holes pipe from Provence)
% gblc_ : galoubet (digit only )
% pt_   : pipe and tabor (3 holes pipe from England)
% xi_   : txistu (3 holes pipe from Euskadi)
%
% you may try it running:
%	abcm2ps sample -e1 -F flute.fmt -T1 -T3

beginps
% -- whistle drawing functions --
%
% start of line 
%	string tw_head
/tw_head{/Helvetica 8 selectfont
	0 15 M 90 rotate(WHISTLE)show -90 rotate
	/Helvetica-Bold 36 selectfont
	0 15 M show .5 SLW newpath}!
/tw_under{
	-2.5 6.5 M 2.5 -2.5 RL 2.5 2.5 RL
	-2.5 -2.5 RM 0 6 RL stroke}!
/tw_over{
	-2.5 7.5 M 2.5 2.5 RL 2.5 -2.5 RL
	-2.5 2.5 RM 0 -6 RL stroke}!
/tw_p{-3 0 M 6 0 RL -3 -3 RM 0 6 RL stroke}!
/tw_pp{-3 0 M 6 0 RL
	-1.5 -1.5 RM 0 3 RL
	-3 0 RM 0 -3 RL stroke}!
% holes
/tw_0{0 0 3 0 360 arc stroke}!
/tw_1{0 0 3 90 270 arc fill 0 0 3 270 90 arc stroke}!
/tw_2{0 0 3 0 360 arc fill}!
% hole table - from C to B
/tw_holes
	[8#222222 8#222221 8#222220 8#222210 8#222200 8#222000
	 8#221000 8#220000 8#210000 8#200000 8#022000 8#000000]
	def
% draw a note
%	octave pitch x tw_note
/tw_note{gsave
	7 T			% oct pit
	cvi			% (if microtone)
	dup 12 idiv		% oct pit pit/12
	dup 0 eq{pop}
		{1 eq{tw_p}
		     {tw_pp}ifelse
	}ifelse
	dup 12 eq{
		pop 8#022222	% (special case)
	}{
		12 mod tw_holes exch get
	}ifelse			% oct holes
	0 1 5{pop
		0 7 T
		dup 8#3 and dup 0 eq{pop tw_0}
				    {1 eq{tw_1}
					 {tw_2}ifelse
		}ifelse
		-3 bitshift
	}for
	pop			% oct
	dup 0 gt{pop tw_over}
		 {0 lt{tw_under}if
	}ifelse
	grestore
}!
% -- galoubet en Do --
%
% début de ligne
%	string gbl_head
/gbl_tete{/Helvetica 7 selectfont
	0 10 M 90 rotate(GALOUBET)show -90 rotate
	/Helvetica-Bold 24 selectfont
	0 10 M show .5 SLW newpath}!
/gbl_c{/Helvetica-utf8 14 selectfont
	-5 10 M show .5 SLW newpath}!
/gbl_doux{-3.5 0 M 7 0 RL stroke}!
/gbl_fort{-3.5 0 M 7 0 RL -3.5 -3.5 RM 0 7 RL stroke}!
/gbl_tres_fort{-5 0 M 10 0 RL
	-2.5 -3 RM 0 6 RL
	-5 0 RM 0 -6 RL stroke}!
% trous
/gbl_0{0 0 3.5 0 360 arc stroke}!
/gbl_1{0 0 3.5 90 270 arc fill 0 0 3.5 270 90 arc stroke}!
/gbl_2{0 0 3.5 0 360 arc fill}!
% table des trous - de E à d'
/gbl_trous
	[8#22210
% F.. B
	 8#22200 8#22100 8#22000 8#21000 8#20000 8#10000 8#00000
% c .. e
	 8#22201 8#22101 8#22001 8#21001 8#20001
% f ..b
	 8#22202 8#00002 8#22002 8#21002 8#20002 8#10202 8#00202
% c' .. d'
	 8#22203 8#22103 8#22003]
	def
% table des doigtés chiffrés
/gbl_dgt
	[(3Þ)
	(3) (2Þ) (2) (1Þ) (1) (Þ) (0)
	(3) (2Þ) (2) (1Þ) (1)
	(3) (0) (2) (1Þ) (1) (ÞF) (F)
	(3) (2Þ) (2)]
	def
% dessin d'une note
%	octave pitch x gbl_note
/gbl_note{gsave
	8 T			% oct pit
	cvi			% (if microtone)
	4 sub			% oct pit/'E'
	exch			% pit/E oct
	0 eq			% (1ere octave seulement)
	1 index 0 ge and	% (pitch = 0 .. 22 = E .. d')
	1 index 23 lt and{
		dup gbl_dgt exch get
		0 30 T gbl_c	0 -35 T		
		gbl_trous exch get
		dup 8#3 and 
			dup 0 eq{pop gbl_doux}
				{dup 1 eq{pop}
					{2 eq{gbl_fort}
					     {gbl_tres_fort}ifelse
				}ifelse
			}ifelse
		-3 bitshift
		0 9 T
		dup 8#3 and 1 eq{gbl_1}if
		-3 bitshift
		0 1 2{pop
			0 9 T
			dup 8#3 and dup 0 eq{pop gbl_0}
					    {1 eq{gbl_1}
						 {gbl_2}ifelse
			}ifelse
			-3 bitshift
		}for
	}if
	pop grestore
}!
% -- galoubet en Do Tablature chiffrée--
%
% début de ligne
%	string gbl_head
/gblc_tete{/Helvetica 7 selectfont
	0 10 M 90 rotate(GALOUBET)show -90 rotate
	/Helvetica-Bold 24 selectfont
	0 10 M show .5 SLW newpath}!
/gblc_c{/Helvetica-utf8 18 selectfont
	-5 10 M show .5 SLW newpath}!
/gblc_doux{-3.5 0 M 7 0 RL stroke}!
/gblc_fort{-3.5 0 M 7 0 RL -3.5 -3.5 RM 0 7 RL stroke}!
/gblc_tres_fort{-5 0 M 10 0 RL
	-2.5 -3 RM 0 6 RL
	-5 0 RM 0 -6 RL stroke}!
% trous
/gblc_0{0 0 3.5 0 360 arc stroke}!
/gblc_1{0 0 3.5 90 270 arc fill 0 0 3.5 270 90 arc stroke}!
/gblc_2{0 0 3.5 0 360 arc fill}!
% table des trous - de E à d'
/gblc_trous
	[8#22210
% F.. B
	 8#22200 8#22100 8#22000 8#21000 8#20000 8#10000 8#00000
% c .. e
	 8#22201 8#22101 8#22001 8#21001 8#20001
% f ..b
	 8#22202 8#00002 8#22002 8#21002 8#20002 8#10202 8#00202
% c' .. d'
	 8#22203 8#22103 8#22003]
	def
% table des doigtés chiffrés
/gblc_dgt
	[(3Þ)
	(3) (2Þ) (2) (1Þ) (1) (Þ) (0)
	(3) (2Þ) (2) (1Þ) (1)
	(3) (0) (2) (1Þ) (1) (ÞF) (F)
	(3) (2Þ) (2)]
	def
% dessin d'une note
%	octave pitch x gblc_note
/gblc_note{gsave
	8 T			% oct pit
	cvi			% (if microtone)
	4 sub			% oct pit/'E'
	exch			% pit/E oct
	0 eq			% (1ere octave seulement)
	1 index 0 ge and	% (pitch = 0 .. 22 = E .. d')
	1 index 23 lt and{
		dup gblc_dgt exch get
		0 10 T gblc_c	0 0 T		
		gblc_trous exch get
		dup 8#3 and 
			dup 0 eq{pop gblc_doux}
				{dup 1 eq{pop}
					{2 eq{gblc_fort}
					     {gblc_tres_fort}ifelse
				}ifelse
			}ifelse
		-3 bitshift
%		0 9 T
%		dup 8#3 and 1 eq{gblc_1}if
%		-3 bitshift
%		0 1 2{pop
%			0 9 T
%			dup 8#3 and dup 0 eq{pop gblc_0}
%					    {1 eq{gblc_1}
%						 {gblc_2}ifelse
%			}ifelse
%			-3 bitshift
%		}for
	}if
	pop grestore
}!
% -- Pipe-Tabor en Do --
%
% début de ligne
%	string pt_head
/pt_tete{/Helvetica 7 selectfont
	0 10 M 90 rotate(PIPE-TABOR)show -90 rotate
	/Helvetica-Bold 24 selectfont
	0 10 M show .5 SLW newpath}!
/pt_doux{-3.5 0 M 7 0 RL stroke}!
/pt_fort{-3.5 0 M 7 0 RL -3.5 -3.5 RM 0 7 RL stroke}!
/pt_tres_fort{-5 0 M 10 0 RL
	-2.5 -3 RM 0 6 RL
	-5 0 RM 0 -6 RL stroke}!
% trous
/pt_0{0 0 3.5 0 360 arc stroke}!
/pt_1{0 0 3.5 90 270 arc fill 0 0 3.5 270 90 arc stroke}!
/pt_2{0 0 3.5 0 360 arc fill}!
% table des trous - de E à d'
/pt_trous
	[8#22210
% F.. B
	 8#22200 8#22100 8#22000 8#20200 8#20000 8#00000 8#22211
% c .. e
	 8#22201 8#22101 8#22001 8#21001 8#20001
% f ..b
	 8#22202 8#22102 8#22002 8#21002 8#20202 8#00202 8#22002
% c' .. d'
	 8#22203 8#22103 8#00203]
	def
% dessin d'une note
%	octave pitch x pt_note
/pt_note{gsave
	8 T			% oct pit
	cvi			% (if microtone)
	11 sub			% oct pit/'E'
	exch			% pit/E oct
	0 eq			% (1ere octave seulement)
	1 index 0 ge and	% (pitch = 0 .. 22 = E .. d')
	1 index 23 lt and{
		pt_trous exch get
		dup 8#3 and 
			dup 0 eq{pop pt_doux}
				{dup 1 eq{pop}
					{2 eq{pt_fort}
					     {pt_tres_fort}ifelse
				}ifelse
			}ifelse
		-3 bitshift
		0 9 T
		dup 8#3 and 1 eq{pt_1}if
		-3 bitshift
		0 1 2{pop
			0 9 T
			dup 8#3 and dup 0 eq{pop pt_0}
					    {1 eq{pt_1}
						 {pt_2}ifelse
			}ifelse
			-3 bitshift
		}for
	}if
	pop grestore
}!
% -- Galoubet en dièze ou Txistu --
%
% début de ligne
%	string xi_head
/xi_tete{/Helvetica 7 selectfont
	0 10 M 90 rotate(TXISTU)show -90 rotate
	/Helvetica-Bold 24 selectfont
	0 10 M show .5 SLW newpath}!
/xi_doux{-3.5 0 M 7 0 RL stroke}!
/xi_fort{-3.5 0 M 7 0 RL -3.5 -3.5 RM 0 7 RL stroke}!
/xi_tres_fort{-5 0 M 10 0 RL
	-2.5 -3 RM 0 6 RL
	-5 0 RM 0 -6 RL stroke}!
% trous
/xi_0{0 0 3.5 0 360 arc stroke}!
/xi_1{0 0 3.5 90 270 arc fill 0 0 3.5 270 90 arc stroke}!
/xi_2{0 0 3.5 0 360 arc fill}!
% table des trous - de E à d'
/xi_trous
	[8#22210
% E.. _B
	 8#22200 8#22100 8#22000 8#20000 8#02200 8#00200 8#22210
% B .. ^d
	 8#22201 8#22101 8#22001 8#20001 8#02001
% e ..g
	 8#22202 8#22102 8#22002 8#20002 
% c' .. d'
	 8#22203 8#00203 8#22213 8#22203 8#22103 8#22003]
	def
% dessin d'une note
%	octave pitch xi tw_note
/xi_note{gsave
	8 T			% oct pit
	cvi			% (if microtone)
	11 sub			% oct pit/'E'
	exch			% pit/E oct
	0 eq			% (1ere octave seulement)
	1 index 0 ge and	% (pitch = 0 .. 22 = E .. d')
	1 index 23 lt and{
		xi_trous exch get
		dup 8#3 and 
			dup 0 eq{pop xi_doux}
				{dup 1 eq{pop}
					{2 eq{xi_fort}
					     {xi_tres_fort}ifelse
				}ifelse
			}ifelse
		-3 bitshift
		0 9 T
		dup 8#3 and 1 eq{xi_1}if
		-3 bitshift
		0 1 2{pop
			0 9 T
			dup 8#3 and dup 0 eq{pop xi_0}
					    {1 eq{xi_1}
						 {xi_2}ifelse
			}ifelse
			-3 bitshift
		}for
	}if
	pop grestore
}!
endps

% all tin whistle transpositions
tablature #1 pitch=D 35 0 63 tw_head tw_note
tablature #2 pitch=C 35 0 63 tw_head tw_note
tablature #3 pitch=Eb 60 0 63 tw_head tw_note
%tablature #4 pitch=Bb, 60 0 63 tw_head tw_note
%tablature #5 pitch=F, 35 0 63 tw_head tw_note
%tablature #6 pitch=G, 35 0 63 tw_head tw_note
%tablature #7 pitch=A, 35 0 63 tw_head tw_note

% galoubet
%tablature #3 pitch=C 30 0 60 gbl_tete gbl_note
tablature #4 pitch=B,b 50 0 60 gbl_tete gbl_note
tablature #5 pitch=G, 30 0 54 pt_tete pt_note
tablature #6 pitch=C 30 0 46 gblc_tete gblc_note
abcm2ps-8.5.2/format.c0000644000175000017500000010131712461146773012621 0ustar  jefjef/*
 * Formatting functions.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2015 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.
 */

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

#include "abc2ps.h"

struct FORMAT cfmt;		/* current format for output */

char *fontnames[MAXFONTS];		/* list of font names */
static char font_enc[MAXFONTS];		/* font encoding */
static char def_font_enc[MAXFONTS];	/* default font encoding */
static char used_font[MAXFONTS];	/* used fonts */
static float swfac_font[MAXFONTS];	/* width scale */
static int nfontnames;
static float staffwidth;

/* format table */
static struct format {
	char *name;
	void *v;
	char type;
#define FORMAT_I 0	/* int */
#define FORMAT_R 1	/* float */
#define FORMAT_F 2	/* font spec */
#define FORMAT_U 3	/* float with unit */
#define FORMAT_B 4	/* boolean */
#define FORMAT_S 5	/* string */
	char subtype;		/* special cases - see code */
	short lock;
} format_tb[] = {
	{"abc2pscompat", &cfmt.abc2pscompat, FORMAT_B, 3},
	{"alignbars", &cfmt.alignbars, FORMAT_I, 0},
	{"aligncomposer", &cfmt.aligncomposer, FORMAT_I, 0},
	{"annotationfont", &cfmt.font_tb[ANNOTATIONFONT], FORMAT_F, 0},
	{"autoclef", &cfmt.autoclef, FORMAT_B, 0},
	{"barsperstaff", &cfmt.barsperstaff, FORMAT_I, 0},
	{"bgcolor", &cfmt.bgcolor, FORMAT_S, 0},
	{"botmargin", &cfmt.botmargin, FORMAT_U, 0},
	{"breaklimit", &cfmt.breaklimit, FORMAT_R, 3},
	{"breakoneoln", &cfmt.breakoneoln, FORMAT_B, 0},
	{"bstemdown", &cfmt.bstemdown, FORMAT_B, 0},
	{"cancelkey", &cfmt.cancelkey, FORMAT_B, 0},
	{"combinevoices", &cfmt.combinevoices, FORMAT_I, 0},
	{"composerfont", &cfmt.font_tb[COMPOSERFONT], FORMAT_F, 0},
	{"composerspace", &cfmt.composerspace, FORMAT_U, 0},
	{"contbarnb", &cfmt.contbarnb, FORMAT_B, 0},
	{"continueall", &cfmt.continueall, FORMAT_B, 0},
	{"custos", &cfmt.custos, FORMAT_B, 0},
	{"dateformat", &cfmt.dateformat, FORMAT_S, 0},
	{"dblrepbar", &cfmt.dblrepbar, FORMAT_I, 2},
	{"dynalign", &cfmt.dynalign, FORMAT_B, 0},
	{"footer", &cfmt.footer, FORMAT_S, 0},
	{"footerfont", &cfmt.font_tb[FOOTERFONT], FORMAT_F, 0},
	{"flatbeams", &cfmt.flatbeams, FORMAT_B, 0},
	{"gchordbox", &cfmt.gchordbox, FORMAT_B, 0},
	{"gchordfont", &cfmt.font_tb[GCHORDFONT], FORMAT_F, 3},
	{"graceslurs", &cfmt.graceslurs, FORMAT_B, 0},
	{"gracespace", &cfmt.gracespace, FORMAT_I, 5},
	{"header", &cfmt.header, FORMAT_S, 0},
	{"headerfont", &cfmt.font_tb[HEADERFONT], FORMAT_F, 0},
	{"historyfont", &cfmt.font_tb[HISTORYFONT], FORMAT_F, 0},
	{"hyphencont", &cfmt.hyphencont, FORMAT_B, 0},
	{"indent", &cfmt.indent, FORMAT_U, 0},
	{"infofont", &cfmt.font_tb[INFOFONT], FORMAT_F, 0},
	{"infoline", &cfmt.infoline, FORMAT_B, 0},
	{"infospace", &cfmt.infospace, FORMAT_U, 0},
	{"keywarn", &cfmt.keywarn, FORMAT_B, 0},
	{"landscape", &cfmt.landscape, FORMAT_B, 0},
	{"leftmargin", &cfmt.leftmargin, FORMAT_U, 0},
	{"lineskipfac", &cfmt.lineskipfac, FORMAT_R, 0},
	{"linewarn", &cfmt.linewarn, FORMAT_B, 0},
	{"maxshrink", &cfmt.maxshrink, FORMAT_R, 2},
	{"maxstaffsep", &cfmt.maxstaffsep, FORMAT_U, 0},
	{"maxsysstaffsep", &cfmt.maxsysstaffsep, FORMAT_U, 0},
	{"measurebox", &cfmt.measurebox, FORMAT_B, 0},
	{"measurefirst", &cfmt.measurefirst, FORMAT_I, 0},
	{"measurefont", &cfmt.font_tb[MEASUREFONT], FORMAT_F, 2},
	{"measurenb", &cfmt.measurenb, FORMAT_I, 0},
	{"micronewps", &cfmt.micronewps, FORMAT_B, 0},
	{"musicspace", &cfmt.musicspace, FORMAT_U, 0},
	{"notespacingfactor", &cfmt.notespacingfactor, FORMAT_R, 1},
	{"oneperpage", &cfmt.oneperpage, FORMAT_B, 0},
	{"pageheight", &cfmt.pageheight, FORMAT_U, 0},
	{"pagewidth", &cfmt.pagewidth, FORMAT_U, 0},
#ifdef HAVE_PANGO
	{"pango", &cfmt.pango, FORMAT_B, 2},
#endif
	{"parskipfac", &cfmt.parskipfac, FORMAT_R, 0},
	{"partsbox", &cfmt.partsbox, FORMAT_B, 0},
	{"partsfont", &cfmt.font_tb[PARTSFONT], FORMAT_F, 1},
	{"partsspace", &cfmt.partsspace, FORMAT_U, 0},
	{"pdfmark", &cfmt.pdfmark, FORMAT_I, 0},
	{"repeatfont", &cfmt.font_tb[REPEATFONT], FORMAT_F, 0},
	{"rightmargin", &cfmt.rightmargin, FORMAT_U, 0},
	{"scale", &cfmt.scale, FORMAT_R, 0},
	{"setdefl", &cfmt.setdefl, FORMAT_B, 0},
//	{"shifthnote", &cfmt.shiftunison, FORMAT_B, 0},	/*to remove*/
	{"shiftunison", &cfmt.shiftunison, FORMAT_I, 0},
	{"slurheight", &cfmt.slurheight, FORMAT_R, 0},
	{"splittune", &cfmt.splittune, FORMAT_B, 0},
	{"squarebreve", &cfmt.squarebreve, FORMAT_B, 0},
	{"staffnonote", &cfmt.staffnonote, FORMAT_I, 0},
	{"staffsep", &cfmt.staffsep, FORMAT_U, 0},
	{"staffwidth", &staffwidth, FORMAT_U, 1},
	{"stemheight", &cfmt.stemheight, FORMAT_R, 0},
	{"straightflags", &cfmt.straightflags, FORMAT_B, 0},
	{"stretchlast", &cfmt.stretchlast, FORMAT_R, 2},
	{"stretchstaff", &cfmt.stretchstaff, FORMAT_B, 0},
	{"subtitlefont", &cfmt.font_tb[SUBTITLEFONT], FORMAT_F, 0},
	{"subtitlespace", &cfmt.subtitlespace, FORMAT_U, 0},
	{"sysstaffsep", &cfmt.sysstaffsep, FORMAT_U, 0},
	{"tempofont", &cfmt.font_tb[TEMPOFONT], FORMAT_F, 0},
	{"textfont", &cfmt.font_tb[TEXTFONT], FORMAT_F, 0},
	{"textoption", &cfmt.textoption, FORMAT_I, 4},
	{"textspace", &cfmt.textspace, FORMAT_U, 0},
	{"titlecaps", &cfmt.titlecaps, FORMAT_B, 0},
	{"titlefont", &cfmt.font_tb[TITLEFONT], FORMAT_F, 0},
	{"titleformat", &cfmt.titleformat, FORMAT_S, 0},
	{"titleleft", &cfmt.titleleft, FORMAT_B, 0},
	{"titlespace", &cfmt.titlespace, FORMAT_U, 0},
	{"titletrim", &cfmt.titletrim, FORMAT_B, 0},
	{"timewarn", &cfmt.timewarn, FORMAT_B, 0},
	{"topmargin", &cfmt.topmargin, FORMAT_U, 0},
	{"topspace", &cfmt.topspace, FORMAT_U, 0},
	{"tuplets", &cfmt.tuplets, FORMAT_I, 3},
	{"vocalfont", &cfmt.font_tb[VOCALFONT], FORMAT_F, 0},
	{"vocalspace", &cfmt.vocalspace, FORMAT_U, 0},
	{"voicefont", &cfmt.font_tb[VOICEFONT], FORMAT_F, 0},
	{"wordsfont", &cfmt.font_tb[WORDSFONT], FORMAT_F, 0},
	{"wordsspace", &cfmt.wordsspace, FORMAT_U, 0},
	{"writefields", &cfmt.fields, FORMAT_B, 1},
	{0, 0, 0, 0}		/* end of table */
};

/* -- search a font and add it if not yet defined -- */
static int get_font(const char *fname, int encoding)
{
	int fnum;

	/* get or set the default encoding */
	for (fnum = nfontnames; --fnum >= 0; )
		if (strcmp(fname, fontnames[fnum]) == 0) {
			if (encoding < 0)
				encoding = def_font_enc[fnum];
			if (encoding == font_enc[fnum])
				return fnum;		/* font found */
			break;
		}
	while (--fnum >= 0) {
		if (strcmp(fname, fontnames[fnum]) == 0
		 && encoding == font_enc[fnum])
			return fnum;
	}

	/* add the font */
	if (nfontnames >= MAXFONTS) {
		error(1, NULL, "Too many fonts");
		return 0;
	}
	if (file_initialized> 0
	 && (epsf <= 1 && !svg))
		error(1, NULL,
		      "Cannot have a new font when the output file is opened");
	fnum = nfontnames++;
	fontnames[fnum] = strdup(fname);
	if (encoding < 0)
		encoding = 0;
	font_enc[fnum] = encoding;
	return fnum;
}

/* -- set a dynamic font -- */
static int dfont_set(struct FONTSPEC *f)
{
	int i;

	for (i = FONT_DYN; i < cfmt.ndfont; i++) {
		if (cfmt.font_tb[i].fnum == f->fnum
		    && cfmt.font_tb[i].size == f->size)
			return i;
	}
	if (i >= FONT_MAX - 1) {
		error(1, NULL, "Too many dynamic fonts");
		return FONT_MAX - 1;
	}
	memcpy(&cfmt.font_tb[i], f, sizeof cfmt.font_tb[0]);
	cfmt.ndfont = i + 1;
	return i;
}

/* -- define a font -- */
static void fontspec(struct FONTSPEC *f,
		     const char *name,
		     int encoding,
		     float size)
{
	if (name)
		f->fnum = get_font(name, encoding);
	else
		name = fontnames[f->fnum];
	f->size = size;
	f->swfac = size;
	if (swfac_font[f->fnum] != 0) {
		f->swfac *= swfac_font[f->fnum];
	} else if (strncmp(name, "Times", 5) == 0) {
		if (strcmp(name, "Times-Bold") == 0)
			f->swfac *= 1.05;
	} else if (strcmp(name, "Helvetica-Bold") == 0) {
		f->swfac *= 1.15;
	} else if (strncmp(name, "Helvetica", 9) == 0
		|| strncmp(name, "Palatino", 8) == 0) {
		f->swfac *= 1.1;
	} else if (strncmp(name, "Courier", 7) == 0) {
		f->swfac *= 1.35;
	} else {
		f->swfac *= 1.1;		/* unknown font */
	}
	if (f == &cfmt.font_tb[GCHORDFONT])
		cfmt.gcf = dfont_set(f);
	else if (f == &cfmt.font_tb[ANNOTATIONFONT])
		cfmt.anf = dfont_set(f);
	else if (f == &cfmt.font_tb[VOCALFONT])
		cfmt.vof = dfont_set(f);
}

/* -- output the font definitions with their encodings -- */
/* This output must occurs after user PostScript definitions because
 * these ones may change the default behaviour */
void define_fonts(void)
{
	int i;
	static char *mkfont =
	"/mkfont{findfont dup length 1 add dict begin\n"
	"	{1 index/FID ne{def}{pop pop}ifelse}forall\n"
	"	CharStrings/double_sharp known not{\n"
	"	    /CharStrings CharStrings dup length dict copy def\n"
	"	    FontMatrix 0 get 1 eq{\n"
	"		CharStrings/sharp{pop .46 0 setcharwidth .001 dup scale usharp ufill}bind put\n"
	"		CharStrings/flat{pop .46 0 setcharwidth .001 dup scale uflat ufill}bind put\n"
	"		CharStrings/natural{pop .40 0 setcharwidth .001 dup scale unat ufill}bind put\n"
	"		CharStrings/double_sharp{pop .46 0 setcharwidth .001 dup scale udblesharp ufill}bind put\n"
	"		CharStrings/double_flat{pop .50 0 setcharwidth .001 dup scale udbleflat ufill}bind put\n"
	"	    }{\n"
	"		CharStrings/sharp{pop 460 0 setcharwidth usharp ufill}bind put\n"
	"		CharStrings/flat{pop 460 0 setcharwidth uflat ufill}bind put\n"
	"		CharStrings/natural{pop 400 0 setcharwidth unat ufill}bind put\n"
	"		CharStrings/double_sharp{pop 460 0 setcharwidth udblesharp ufill}bind put\n"
	"		CharStrings/double_flat{pop 500 0 setcharwidth udbleflat ufill}bind put\n"
	"	    }ifelse\n"
	"	}if currentdict definefont pop end}!\n";

	fputs(mkfont, fout);
	make_font_list();
	for (i = 0; i < nfontnames; i++) {
		if (used_font[i])
			define_font(fontnames[i], i, font_enc[i]);
	}
}

/* -- mark the used fonts -- */
void make_font_list(void)
{
	struct FORMAT *f;
	int i;

	f = &cfmt;
	for (i = FONT_UMAX; i < FONT_DYN; i++)
		used_font[f->font_tb[i].fnum] = 1;
}

/* -- set the name of an information header type -- */
/* the argument is
 *	<letter> [ <possibly quoted string> ]
 * this information is kept in the 'I' information */
static void set_infoname(char *p)
{
	struct SYMBOL *s, *prev;

	if (*p == 'I')
		return;
	s = info['I' - 'A'];
	prev = NULL;
	while (s) {
		if (s->as.text[0] == *p)
			break;
		prev = s;
		s = s->next;
	}
	if (p[1] == '\0') {		/* if delete */
		if (s) {
			if (!prev)
				info['I' - 'A'] = s->next;
			else if ((prev->next = s->next) != 0)
				prev->next->prev = prev;
		}
		return;
	}
	if (!s) {
		s = (struct SYMBOL *) getarena(sizeof *s);
		memset(s, 0, sizeof *s);
		if (!prev)
			info['I' - 'A'] = s;
		else {
			prev->next = s;
			s->prev = prev;
		}
	}
	s->as.text = (char *) getarena(strlen(p) + 1);
	strcpy(s->as.text, p);
}

/* -- set the default format -- */
/* this function is called only once, at abcm2ps startup time */
void set_format(void)
{
	struct FORMAT *f;

	static const char helvetica[] = "Helvetica";
	static const char times[] = "Times-Roman";
	static const char times_bold[] = "Times-Bold";
	static const char times_italic[] = "Times-Italic";
	static const char sans[] = "sans-serif";
	static const char serif[] = "serif";
	static const char serif_italic[] = "serif-Italic";
	static const char serif_bold[] = "serif-Bold";

	f = &cfmt;
	memset(f, 0, sizeof *f);
	f->pageheight = PAGEHEIGHT;
	f->pagewidth = PAGEWIDTH;
	f->leftmargin = MARGIN;
	f->rightmargin = MARGIN;
	f->topmargin = 1.0 CM;
	f->botmargin = 1.0 CM;
	f->topspace = 0.8 CM;
	f->titlespace = 0.2 CM;
	f->subtitlespace = 0.1 CM;
	f->composerspace = 0.2 CM;
	f->musicspace = 0.2 CM;
	f->partsspace = 0.3 CM;
	f->staffsep = 46.0 PT;
	f->sysstaffsep = 34.0 PT;
	f->maxstaffsep = 2000.0 PT;
	f->maxsysstaffsep = 2000.0 PT;
	f->vocalspace = 23.0 PT;
	f->textspace = 0.5 CM;
	f->scale = 0.75;
	f->slurheight = 1.0;
	f->maxshrink = 0.65;
	f->breaklimit = 0.7;
	f->stretchlast = 0.25;
	f->stretchstaff = 1;
	f->graceslurs = 1;
	f->hyphencont = 1;
	f->lineskipfac = 1.1;
	f->parskipfac = 0.4;
	f->measurenb = -1;
	f->measurefirst = 1;
	f->autoclef = 1;
	f->breakoneoln = 1;
	f->dblrepbar = (B_COL << 12) + (B_CBRA << 8) + (B_OBRA << 4) + B_COL;
	f->dynalign = 1;
	f->keywarn = 1;
	f->linewarn = 1;
#ifdef HAVE_PANGO
	if (!svg && epsf <= 1)
		f->pango = 1;
	else
		lock_fmt(&cfmt.pango);	/* SVG output does not use panga */
#endif
	f->staffnonote = 1;
	f->titletrim = 1;
	f->aligncomposer = A_RIGHT;
	f->notespacingfactor = 1.414;
	f->stemheight = STEM;
#ifndef WIN32
	f->dateformat = strdup("%b %e, %Y %H:%M");
#else
	f->dateformat = strdup("%b %#d, %Y %H:%M");
#endif
	f->gracespace = (65 << 16) | (80 << 8) | 120;	/* left-inside-right - unit 1/10 pt */
	f->textoption = T_LEFT;
	f->ndfont = FONT_DYN;
	if (svg || epsf > 2) {		// SVG output
		fontspec(&f->font_tb[ANNOTATIONFONT], sans, 0, 12.0);
		fontspec(&f->font_tb[COMPOSERFONT], serif_italic, 0, 14.0);
		fontspec(&f->font_tb[FOOTERFONT], serif, 0, 12.0); /* not scaled */
		fontspec(&f->font_tb[GCHORDFONT], sans, 0, 12.0);
		fontspec(&f->font_tb[HEADERFONT], serif, 0, 12.0); /* not scaled */
		fontspec(&f->font_tb[HISTORYFONT], serif, 0, 16.0);
		fontspec(&f->font_tb[INFOFONT],	serif_italic, 0, 14.0); /* same as composer by default */
		fontspec(&f->font_tb[MEASUREFONT], serif_italic, 0, 14.0);
		fontspec(&f->font_tb[PARTSFONT], serif, 0, 15.0);
		fontspec(&f->font_tb[REPEATFONT], serif, 0, 13.0);
		fontspec(&f->font_tb[SUBTITLEFONT], serif, 0, 16.0);
		fontspec(&f->font_tb[TEMPOFONT], serif_bold, 0, 15.0);
		fontspec(&f->font_tb[TEXTFONT],	serif, 0, 16.0);
		fontspec(&f->font_tb[TITLEFONT], serif, 0, 20.0);
		fontspec(&f->font_tb[VOCALFONT], serif_bold, 0, 13.0);
		fontspec(&f->font_tb[VOICEFONT], serif_bold, 0, 13.0);
		fontspec(&f->font_tb[WORDSFONT], serif, 0, 16.0);
	} else {			// PS output
		fontspec(&f->font_tb[ANNOTATIONFONT], helvetica, 0, 12.0);
		fontspec(&f->font_tb[COMPOSERFONT], times_italic, 0, 14.0);
		fontspec(&f->font_tb[FOOTERFONT], times, 0, 12.0); /* not scaled */
		fontspec(&f->font_tb[GCHORDFONT], helvetica, 0, 12.0);
		fontspec(&f->font_tb[HEADERFONT], times, 0, 12.0); /* not scaled */
		fontspec(&f->font_tb[HISTORYFONT], times, 0, 16.0);
		fontspec(&f->font_tb[INFOFONT],	times_italic, 0, 14.0); /* same as composer by default */
		fontspec(&f->font_tb[MEASUREFONT], times_italic, 0, 14.0);
		fontspec(&f->font_tb[PARTSFONT], times, 0, 15.0);
		fontspec(&f->font_tb[REPEATFONT], times, 0, 13.0);
		fontspec(&f->font_tb[SUBTITLEFONT], times, 0, 16.0);
		fontspec(&f->font_tb[TEMPOFONT], times_bold, 0, 15.0);
		fontspec(&f->font_tb[TEXTFONT],	times, 0, 16.0);
		fontspec(&f->font_tb[TITLEFONT], times, 0, 20.0);
		fontspec(&f->font_tb[VOCALFONT], times_bold, 0, 13.0);
		fontspec(&f->font_tb[VOICEFONT], times_bold, 0, 13.0);
		fontspec(&f->font_tb[WORDSFONT], times, 0, 16.0);
	}
	f->fields[0] = (1 << ('C' - 'A'))
		| (1 << ('M' - 'A'))
		| (1 << ('O' - 'A'))
		| (1 << ('P' - 'A'))
		| (1 << ('Q' - 'A'))
		| (1 << ('T' - 'A'))
		| (1 << ('W' - 'A'));
	f->fields[1] = (1 << ('w' - 'a'));
	set_infoname("R \"Rhythm: \"");
	set_infoname("B \"Book: \"");
	set_infoname("S \"Source: \"");
	set_infoname("D \"Discography: \"");
	set_infoname("N \"Notes: \"");
	set_infoname("Z \"Transcription: \"");
	set_infoname("H \"History: \"");
}

/* -- print the current format -- */
void print_format(void)
{
	struct format *fd;
static char *yn[2] = {"no","yes"};

	for (fd = format_tb; fd->name; fd++) {
		printf("%-15s ", fd->name);
		switch (fd->type) {
		case FORMAT_B:
			switch (fd->subtype) {
#ifdef HAVE_PANGO
			case 2:				/* pango = 0, 1 or 2 */
				if (cfmt.pango == 2) {
					printf("2\n");
					break;
				}
				/* fall thru */
#endif
			default:
			case 0:
				printf("%s\n", yn[*((int *) fd->v)]);
				break;
			case 1: {			/* writefields */
				int i;

				for (i = 0; i < 32; i++) {
					if (cfmt.fields[0] & (1 << i))
						printf("%c", (char) ('A' + i));
					if (cfmt.fields[1] & (1 << i))
						printf("%c", (char) ('a' + i));
				}
				printf("\n");
				break;
			    }
			}
			break;
		case FORMAT_I:
			switch (fd->subtype) {
			default:
				printf("%d\n", *((int *) fd->v));
				break;
			case 2: {		/* dblrepbar */
				int v;
				char tmp[16], *p;

				p = &tmp[sizeof tmp - 1];
				*p = '\0';
				for (v = cfmt.dblrepbar; v != 0; v >>= 4) {
					switch (v & 0x0f) {
					case B_BAR:
						*--p = '|';
						break;
					case B_OBRA:
						*--p = '[';
						break;
					case B_CBRA:
						*--p = ']';
						break;
					default:
//					case B_COL:
						*--p = ':';
						break;
					}
				}
				printf("%s\n", p);
				break;
			    }
			case 3:			/* tuplets */
				printf("%d %d %d\n",
					cfmt.tuplets >> 8,
					(cfmt.tuplets >> 4) & 0x0f,
					cfmt.tuplets & 0x0f);
				break;
//			case 4:			/* textoption */
//				break;
			case 5:			/* gracespace */
				printf("%d.%d %d.%d %d.%d\n",
					(cfmt.gracespace >> 16) / 10,
					(cfmt.gracespace >> 16) % 10,
					((cfmt.gracespace >> 8) & 0xff) / 10,
					((cfmt.gracespace >> 8) & 0xff) % 10,
					(cfmt.gracespace & 0xff) / 10,
					(cfmt.gracespace & 0xff) % 10);
				break;
			}
			break;
		case FORMAT_R:
			printf("%.2f\n", *((float *) fd->v));
			break;
		case FORMAT_F: {
			struct FONTSPEC *s;

			s = (struct FONTSPEC *) fd->v;
			printf("%s", fontnames[s->fnum]);
			printf(" %s", font_enc[s->fnum] ? "native" : "utf-8");
			printf(" %.1f", s->size);
			if ((fd->subtype == 1 && cfmt.partsbox)
			 || (fd->subtype == 2 && cfmt.measurebox)
			 || (fd->subtype == 3 && cfmt.gchordbox))
				printf(" box");
			printf("\n");
			break;
		}
		case FORMAT_U:
			if (fd->subtype == 0)
				printf("%.2fcm\n", *((float *) fd->v) / (1 CM));
			else
				printf("%.2fcm\n",
					(cfmt.pagewidth
						- cfmt.leftmargin
						- cfmt.rightmargin)
					/ (1 CM));
			break;
		case FORMAT_S:
			printf("\"%s\"\n",
				*((char **) fd->v) != 0 ? *((char **) fd->v) : "");
			break;
		}
	}
}

/* -- get an encoding -- */
static int parse_encoding(char *p)
{
	return strncasecmp(p, "native", 6) == 0;
}

/* -- get a position -- */
static int get_posit(char *p)
{
	if (strcmp(p, "up") == 0
	 || strcmp(p, "above") == 0)
		return SL_ABOVE;
	if (strcmp(p, "down") == 0
	 || strcmp(p, "below") == 0)
		return SL_BELOW;
	if (strcmp(p, "hidden") == 0
	 || strcmp(p, "opposite") == 0)
		return SL_HIDDEN;
	return 0;			/* auto (!= SL_AUTO) */
}

/* -- get the option for text -- */
int get_textopt(char *p)
{
	if (strncmp(p, "align", 5) == 0
	 || strncmp(p, "justify", 7) == 0)
		return T_JUSTIFY;
	if (strncmp(p, "ragged", 6) == 0
	 || strncmp(p, "fill", 4) == 0)
		return T_FILL;
	if (strncmp(p, "center", 6) == 0)
		return T_CENTER;
	if (strncmp(p, "skip", 4) == 0)
		return T_SKIP;
	if (strncmp(p, "right", 5) == 0)
		return T_RIGHT;
	return T_LEFT;
}

/* -- get the double repeat bar -- */
static int get_dblrepbar(char *p)
{
	int bar_type;

	bar_type = 0;
	for (;;) {
		switch (*p++) {
		case '|':
			bar_type <<= 4;
			bar_type |= B_BAR;
			continue;
		case '[':
			bar_type <<= 4;
			bar_type |= B_OBRA;
			continue;
		case ']':
			bar_type <<= 4;
			bar_type |= B_CBRA;
			continue;
		case ':':
			bar_type <<= 4;
			bar_type |= B_COL;
			continue;
		default:
			break;
		}
		break;
	}
	return bar_type;
}

/* -- get a boolean value -- */
static int g_logv(char *p)
{
	switch (*p) {
	case '\0':
	case '1':
	case 'y':
	case 'Y':
	case 't':
	case 'T':
		return 1;
	case '0':
	case 'n':
	case 'N':
	case 'f':
	case 'F':
		break;
	default:
		error(0, NULL, "Unknown logical '%s' - false assumed", p);
		break;
	}
	return 0;
}

/* -- get a font specifier -- */
static void g_fspc(char *p,
		   struct FONTSPEC *f)
{
	char fname[80];
	int encoding;
	float fsize;

	p = get_str(fname, p, sizeof fname);
	if (isalpha((unsigned char) *p) || *p == '*') {
		if (*p == '*')
			encoding = font_enc[f->fnum];
		else
			encoding = parse_encoding(p);
		while (*p != '\0' && !isspace((unsigned char) *p))
			p++;
		while (isspace((unsigned char) *p))
			p++;
	} else {
		encoding = -1;
	}
	fsize = f->size;
	if (*p != '\0' && *p != '*') {
		char *q;
		float v;

		v = strtod(p, &q);
		if (v <= 0 || (*q != '\0' && *q != ' '))
			error(1, NULL, "Bad font size '%s'", p);
		else
			fsize = v;
	}
	fontspec(f,
		 strcmp(fname, "*") != 0 ? fname : 0,
		 encoding,
		 fsize);
	if (file_initialized <= 0)
		used_font[f->fnum] = 1;
	if (f - cfmt.font_tb == outft)
		outft = -1;
#ifdef HAVE_PANGO
	pg_reset_font();
#endif
}

/* -- parse a 'tablature' definition -- */
/* %%tablature
 *	[#<nunmber (1..MAXTBLT)>]
 *	[pitch=<instrument pitch (<note> # | b)>]
 *	[[<head width>]
 *	 <height above>]
 *	<height under>
 *	<head function>
 *	<note function>
 *	[<bar function>]
 */
struct tblt_s *tblt_parse(char *p)
{
	struct tblt_s *tblt;
	int n;
	char *q;
	static char notes_tb[] = "CDEFGABcdefgab";
	static char pitch_tb[14] = {60, 62, 64, 65, 67, 69, 71,
				    72, 74, 76, 77, 79, 81, 83};

	/* number */
	if (*p == '#') {
		p++;
		n = *p++ - '0' - 1;
		if ((unsigned) n >= MAXTBLT
		 || (*p != '\0' && *p != ' ')) {
			error(1, NULL, "Invalid number in %%%%tablature");
			return 0;
		}
		if (*p == '\0')
			return tblts[n];
		while (isspace((unsigned char) *p))
			p++;
	} else {
		n = -1;
	}

	/* pitch */
	tblt = malloc(sizeof *tblt);
	memset(tblt, 0, sizeof *tblt);
	if (strncmp(p, "pitch=", 6) == 0) {
		p += 6;
		if (*p == '^' || *p == '_') {
			if (*p == '^') {
				tblt->pitch++;
				tblt->instr[1] = '#';
			} else {
				tblt->pitch--;
				tblt->instr[1] = 'b';
			}
			p++;
		}
		if (*p == '\0' || (q = strchr(notes_tb, *p)) == NULL) {
			error(1, NULL, "Invalid pitch in %%%%tablature");
			return 0;
		}
		tblt->pitch += pitch_tb[q - notes_tb];
		tblt->instr[0] = toupper(*p++);
		while (*p == '\'' || *p == ',') {
			if (*p++ == '\'')
				tblt->pitch += 12;
			else
				tblt->pitch -= 12;
		}
		if (*p == '#' || *p == 'b') {
			if (*p == '#')
				tblt->pitch++;
			else
				tblt->pitch--;
			tblt->instr[1] = *p++;
		}
		while (*p == '\'' || *p == ',') {
			if (*p++ == '\'')
				tblt->pitch += 12;
			else
				tblt->pitch -= 12;
		}
		while (isspace((unsigned char) *p))
			p++;
	}

	/* width and heights */
	if (!isdigit(*p)) {
		error(1, NULL, "Invalid width/height in %%%%tablature");
		return 0;
	}
	tblt->hu = scan_u(p);
	while (*p != '\0' && !isspace((unsigned char) *p))
		p++;
	while (isspace((unsigned char) *p))
		p++;
	if (isdigit(*p)) {
		tblt->ha = tblt->hu;
		tblt->hu = scan_u(p);
		while (*p != '\0' && !isspace((unsigned char) *p))
			p++;
		while (isspace((unsigned char) *p))
			p++;
		if (isdigit(*p)) {
			tblt->wh = tblt->ha;
			tblt->ha = tblt->hu;
			tblt->hu = scan_u(p);
			while (*p != '\0' && !isspace((unsigned char) *p))
				p++;
			while (isspace((unsigned char) *p))
				p++;
		}
	}
	if (*p == '\0')
		goto err;

	/* PS functions */
	p = strdup(p);
	tblt->head = p;
	while (*p != '\0' && !isspace((unsigned char) *p))
		p++;
	if (*p == '\0')
		goto err;
	*p++ = '\0';
	while (isspace((unsigned char) *p))
		p++;
	tblt->note = p;
	while (*p != '\0' && !isspace((unsigned char) *p))
		p++;
	if (*p != '\0') {
		*p++ = '\0';
		while (isspace((unsigned char) *p))
			p++;
		tblt->bar = p;
		while (*p != '\0' && !isspace((unsigned char) *p))
			p++;
		if (*p != '\0')
			goto err;
	}

	/* memorize the definition */
	if (n >= 0)
		tblts[n] = tblt;
	return tblt;
err:
	error(1, NULL, "Wrong values in %%%%tablature");
	return 0;
}

/* functions to set a voice parameter */
#define F_SET_PAR(param) \
static void set_ ## param(struct VOICE_S *p_voice, int val)\
{\
	p_voice->posit.param = val;\
}
F_SET_PAR(dyn)
F_SET_PAR(gch)
F_SET_PAR(orn)
F_SET_PAR(voc)
F_SET_PAR(vol)
F_SET_PAR(std)
F_SET_PAR(gsd)

struct vpar {
	char *name;
	void (*f)(struct VOICE_S *p_voice, int val);
	int max;
};
static const struct vpar vpar_tb[] = {
	{"dynamic", set_dyn, 3},	/* 0 */
	{"gchord", set_gch, 3},		/* 1 */
	{"gstemdir", set_gsd, 3},	/* 2 */
	{"ornament", set_orn, 3},	/* 3 */
	{"stemdir", set_std, 2},	/* 4 */
	{"vocal", set_voc, 3},		/* 5 */
	{"volume", set_vol, 3},		/* 6 */
#ifndef WIN32
	{}
#else
	{NULL, NULL, 0}
#endif
};
/* -- set a voice parameter -- */
void set_voice_param(struct VOICE_S *p_voice,	/* current voice */
			int state,		/* tune state */
			char *w,		/* keyword */
			char *p)		/* argument */
{
	const struct vpar *vpar, *vpar2 = NULL;
	int i, val;

	for (vpar = vpar_tb; vpar->name; vpar++) {
		if (strcmp(w, vpar->name))
			continue;
		if (!isdigit(*p))
			val = get_posit(p);
		else
			val = strtod(p, 0);
		if ((unsigned) val > vpar->max)
			goto err;
		break;
	}
	if (!vpar->name) {	/* compatibility with previous versions */
		val = -1;
		switch (*w) {
		case 'e':
			if (strcmp(w, "exprabove") == 0) {
				vpar = &vpar[0];	/* dyn */
				vpar2 = &vpar[6];	/* vol */
				if (g_logv(p))
					val = SL_ABOVE;
				else
					val = SL_BELOW;
				break;
			}
			if (strcmp(w, "exprbelow") == 0) {
				vpar = &vpar[0];	/* dyn */
				vpar2 = &vpar[6];	/* vol */
				if (g_logv(p))
					val = SL_BELOW;
				else
					val = SL_ABOVE;
				break;
			}
			break;
		case 'v':
			if (strcmp(w, "vocalabove") == 0) {	/* compatibility */
				vpar = &vpar[5];	/* voc */
				if (g_logv(p))
					val = SL_ABOVE;
				else
					val = SL_BELOW;
				break;
			}
			break;
		}
		if (val < 0)
			goto err;
	}
	if (state == ABC_S_TUNE) {
		vpar->f(p_voice, val);
		if (vpar2)
			vpar2->f(p_voice, val);
		return;
	}
	for (i = MAXVOICE, p_voice = voice_tb;	/* global */
	     --i >= 0;
	     p_voice++) {
		vpar->f(p_voice, val);
		if (vpar2)
			vpar2->f(p_voice, val);
	}
	cfmt.posit = voice_tb[0].posit;
	return;
err:
	error(1, NULL, "Bad value %%%%%s %s", w, p);
}

/* -- parse a format line -- */
void interpret_fmt_line(char *w,		/* keyword */
			char *p,		/* argument */
			int lock)
{
	struct format *fd;

	switch (*w) {
	case 'b':
		if (strcmp(w, "barnumbers") == 0)	/* compatibility */
			w = "measurenb";
		break;
	case 'c':
		if (strcmp(w, "comball") == 0) {	/* compatibility */
			cfmt.combinevoices = 2;
			return;
		}
		break;
	case 'f':
		if (strcmp(w, "font") == 0) {
			int i, fnum, encoding;
			float swfac;
			char fname[80];

			if (file_initialized > 0
			 && !svg && epsf <= 1) {	/* PS */
				error(1, NULL,
				      "Cannot define a font when the output file is opened");
				return;
			}
			p = get_str(fname, p, sizeof fname);
			swfac = 0;			/* defaults to 1.2 */
			encoding = 0;
			if (*p != '\0') {
				if (isalpha((unsigned char) *p)) {
					encoding = parse_encoding(p);
					while (*p != '\0'
					    && !isspace((unsigned char) *p))
						p++;
					while (isspace((unsigned char) *p))
						p++;
				}
				if (isdigit((unsigned char) *p)) {
					char *q;
					float v;

					v = strtod(p, &q);
					if (v > 2 || (*q != '\0' && *q != '\0'))
						goto bad;
					swfac = v;
				}
			}
			fnum = get_font(fname, encoding);
			def_font_enc[fnum] = encoding;
			swfac_font[fnum] = swfac;
			used_font[fnum] = 1;
			for (i = FONT_UMAX; i < FONT_MAX; i++) {
				if (cfmt.font_tb[i].fnum == fnum)
					cfmt.font_tb[i].swfac = cfmt.font_tb[i].size
									* swfac;
			}
			return;
		}
		break;
	case 'i':
		if (strcmp(w, "infoname") == 0) {
			if (*p < 'A' || *p > 'Z')
				goto bad;
			set_infoname(p);
			return;
		}
		break;
	case 'm':
		if (strcmp(w, "musiconly") == 0) {	/* compatibility */
			if (g_logv(p))
				cfmt.fields[1] &= ~(1 << ('w' - 'a'));
			else
				cfmt.fields[1] |= (1 << ('w' - 'a'));
			return;
		}
		break;
	case 'p':
		if (strcmp(w, "printparts") == 0) {	/* compatibility */
			if (g_logv(p))
				cfmt.fields[0] |= (1 << ('P' - 'A'));
			else
				cfmt.fields[0] &= ~(1 << ('P' - 'A'));
			return;
		}
		if (strcmp(w, "printtempo") == 0) {	/* compatibility */
			if (g_logv(p))
				cfmt.fields[0] |= (1 << ('Q' - 'A'));
			else
				cfmt.fields[0] &= ~(1 << ('Q' - 'A'));
			return;
		}
		break;
	case 's':
		if (strncmp(w, "setfont-", 8) == 0) {
			int i;

			i = w[8] - '0';
			if (i < 0 || i >= FONT_UMAX)
				return;
			g_fspc(p, &cfmt.font_tb[i]);
			return;
		}
		break;
	case 'w':
		if (strcmp(w, "withxrefs") == 0) {	/* compatibility */
			if (g_logv(p))
				cfmt.fields[0] |= (1 << ('X' - 'A'));
			else
				cfmt.fields[0] &= ~(1 << ('X' - 'A'));
			return;
		}
		if (strcmp(w, "writehistory") == 0) {	/* compatibility */
			struct SYMBOL *s;
			int bool;
			unsigned u;

			bool = g_logv(p);
			for (s = info['I' - 'A']; s != 0; s = s->next) {
				u = s->as.text[0] - 'A';
				if (bool)
					cfmt.fields[0] |= (1 << u);
				else
					cfmt.fields[0] &= ~(1 << u);
			}
			return;
		}
		break;
	}
	for (fd = format_tb; fd->name; fd++)
		if (strcmp(w, fd->name) == 0)
			break;
	if (fd->name == 0)
		return;

	{
		int l;

		l = strlen(p);
		if (strcmp(p + l - 5, " lock") == 0) {
			p[l - 5] = '\0';
			lock = 1;
		}
	}
	if (lock)
		fd->lock = 1;
	else if (fd->lock)
		return;

	switch (fd->type) {
	case FORMAT_B:
		switch (fd->subtype) {
#ifdef HAVE_PANGO
		case 2:				/* %%pango = 0, 1 or 2 */
			if (*p == '2') {
				cfmt.pango = 2;
				break;
			}
			/* fall thru */
#endif
		default:
		case 0:
		case 3:				/* %%abc2pscompat */
			*((int *) fd->v) = g_logv(p);
			if (fd->subtype == 3) {
				if (cfmt.abc2pscompat)
					deco['M'] = "tenuto";
				else
					deco['M'] = "lowermordent";
			}
			break;
		case 1:	{			/* %%writefields */
			char *q;
			int bool, i;
			unsigned u;

			q = p;
			while (*p != '\0' && !isspace((unsigned char) *p))
				p++;
			while (isspace((unsigned char) *p))
				p++;
			bool = g_logv(p);
			while (*q != '\0' && !isspace((unsigned char) *q)) {
				u = *q - 'A';
				if (u < 26) {
					i = 0;
				} else {
					u = *q - 'a';
					if (u < 26)
						i = 1;
					else
						break;	/*fixme: error */
				}
				if (bool)
					cfmt.fields[i] |= (1 << u);
				else
					cfmt.fields[i] &= ~(1 << u);
				q++;
			}
			break;
		    }
		}
		break;
	case FORMAT_I:
		if (fd->subtype == 3) {		/* tuplets */
			unsigned i1, i2, i3;

			if (sscanf(p, "%d %d %d", &i1, &i2, &i3) != 3
			 || i1 > 2 || i2 > 2 || i3 > 2)
				goto bad;
			cfmt.tuplets = (i1 << 8) | (i2 << 4) | i3;
			break;
		}
		if (fd->subtype == 5) {		/* gracespace */
			unsigned i1, i2, i3;
			float f1, f2, f3;

			if (sscanf(p, "%f %f %f", &f1, &f2, &f3) != 3
			 || f1 > 256 || f2 > 256 || f3 > 256)
				goto bad;
			i1 = f2 * 10;
			i2 = f2 * 10;
			i3 = f3 * 10;
			cfmt.gracespace = (i1 << 16) | (i2 << 8) | i3;
			break;
		}
		if (fd->subtype == 4 && !isdigit(*p))	/* textoption */
			cfmt.textoption = get_textopt(p);
		else if (fd->subtype == 2)		/* dblrepbar */
			cfmt.dblrepbar = get_dblrepbar(p);
		else if (isdigit(*p) || *p == '-' || *p == '+')
			sscanf(p, "%d", (int *) fd->v);
		else
			*((int *) fd->v) = g_logv(p);
		if (fd->subtype == 4) {			/* textoption */
			if (cfmt.textoption < 0) {
				cfmt.textoption = 0;
				goto bad;
			}
		}
		break;
	case FORMAT_R: {
		char *q;
		int i;
		float v;

		v = strtod(p, &q);
		if (*q != '\0' && *q != ' ')
			goto bad;
		switch (fd->subtype) {
		default:
			if (v <= 0)
				goto bad;
			break;
		case 1: {			/* note spacing factor */
			float v2;

			if (v < 1 || v > 2)
				goto bad;
			i = C_XFLAGS;		/* crotchet index */
			v2 = space_tb[i];
			for ( ; --i >= 0; ) {
				v2 /= v;
				space_tb[i] = v2;
			}
			i = C_XFLAGS;
			v2 = space_tb[i];
			for ( ; ++i < NFLAGS_SZ; ) {
				v2 *= v;
				space_tb[i] = v2;
			}
			break;
		    }
		case 2:				/* maxshrink / stretchlast */
			if (v < 0 || v > 1)
				goto bad;
			break;
		case 3:				/* breaklimit */
			if (v < 0.5 || v > 1)
				goto bad;
			break;
		}
		*((float *) fd->v) = v;
		break;
	    }
	case FORMAT_F: {
		int b;

		g_fspc(p, (struct FONTSPEC *) fd->v);
		b = strstr(p, "box") != NULL;
		switch (fd->subtype) {
		case 1:
			cfmt.partsbox = b;
			break;
		case 2:
			cfmt.measurebox = b;
			break;
		case 3:
			cfmt.gchordbox = b;
			break;
		}
		break;
	    }
	case FORMAT_U:
		*((float *) fd->v) = scan_u(p);
		if (fd->subtype == 1) {		/* staffwidth */
			float rmargin;

			rmargin = (cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
					- staffwidth - cfmt.leftmargin;
			if (rmargin < 0)
				error(1, NULL, "'staffwidth' too big\n");
			else
				cfmt.rightmargin = rmargin;
		}
		break;
	case FORMAT_S: {
		int l;

		l = strlen(p) + 1;
		*((char **) fd->v) = getarena(l);
		if (*p == '"')
			get_str(*((char **) fd->v), p, l);
		else
			strcpy(*((char **) fd->v), p);
		break;
	    }
	}
	return;
bad:
	error(1, NULL, "Bad value '%s' for '%s' - ignored", p, w);
}

/* -- lock a format -- */
void lock_fmt(void *fmt)
{
	struct format *fd;

	for (fd = format_tb; fd->name; fd++)
		if (fd->v == fmt)
			break;
	if (fd->name == 0)
		return;
	fd->lock = 1;
}

/* -- start a new font -- */
void set_font(int ft)
{
	int fnum;
	struct FONTSPEC *f, *f2;

	if (ft == outft)
		return;
	f = &cfmt.font_tb[ft];
	if (outft >= 0) {
		f2 = &cfmt.font_tb[outft];
		outft = ft;
		fnum = f->fnum;
		if (fnum == f2->fnum && f->size == f2->size)
			return;
	} else {
		outft = ft;
		fnum = f->fnum;
	}
	if (!used_font[fnum]
	 && epsf <= 1 && !svg) {	/* (not usefull for svg output) */
		if (file_initialized <= 0) {
			used_font[fnum] = 1;
		} else {
			error(1, NULL,
			      "Font '%s' not predefined; using first in list",
			      fontnames[fnum]);
			fnum = 0;
		}
	}
	if (f->size == 0) {
		error(0, NULL, "Font '%s' with a null size - set to 8",
		      fontnames[fnum]);
		f->size = 8;
	}
	a2b("%.1f F%d ", f->size, fnum);
}

/* -- get the encoding of a font -- */
int get_font_encoding(int ft)
{
	return font_enc[ft];
}
abcm2ps-8.5.2/front.c0000644000175000017500000004560712457404263012466 0ustar  jefjef/*
 * ABC front-end parser
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 2011-2015 Jean-François Moine (http://moinejf.free.fr)
 *
 * 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.
 */

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

#ifdef WIN32
#define strncasecmp _strnicmp
#define strdup _strdup
#endif

#include "abc2ps.h"
#include "slre.h"

static unsigned char *dst;
static int offset, size;
static unsigned char *selection;
static int latin, skip;
static char prefix[4] = {'%'};

/*
 * translation table from the ABC draft version 2
 *	` grave
 *	' acute
 *	^ circumflex
 *	, cedilla
 *	" umlaut
 *	~ tilde
 *	o ring
 *	= macron or stroke
 *	/ slash
 *	; ogonek
 *	v caron
 *	u breve
 *	: long Hungarian umlaut
 *	. dot / dotless
 * else, ligatures
 *	ae ss ng
 */
/* !! each table item is 3 bytes: 1 char, 1 UTF-8 char on 2 bytes */
static unsigned char grave[] = "AÀEÈIÌOÒUÙaàeèiìoòuù";
static unsigned char acute[] = "AÁEÉIÍOÓUÚYÝaáeéiíoóuúyýSŚZŹsśzźRŔLĹCĆNŃrŕlĺcćnń";
static unsigned char circumflex[] = "AÂEÊIÎOÔUÛaâeêiîoôuûHĤJĴhĥjĵCĈGĜSŜcĉgĝsŝ";
static unsigned char cedilla[] = "CÇcçSŞsşTŢtţRŖLĻGĢrŗlļgģNŅKĶnņkķ";
static unsigned char umlaut[] = "AÄEËIÏOÖUÜYŸaäeëiïoöuüyÿ";
static unsigned char tilde[] = "AÃNÑOÕaãnñoõIĨiĩUŨuũ";
static unsigned char ring[] = "AÅaåUŮuů";
static unsigned char macron[] = "AĀDĐEĒHĦIĪOŌTŦUŪaādđeēhħiīoōtŧuū"; /* and stroke! */
static unsigned char slash[] = "OØoøDĐdđLŁlł";
static unsigned char ogonek[] = "AĄEĘIĮUŲaąeęiįuų";
static unsigned char caron[] = "LĽSŠTŤZŽlľsštťzžCČEĚDĎNŇRŘcčeědďnňrř";
static unsigned char breve[] = "AĂaăEĔeĕGĞgğIĬiĭOŎoŏUŬuŭ";
static unsigned char hungumlaut[] = "OŐUŰoőuű";
static unsigned char dot[] = "ZŻzżIİiıCĊcċGĠgġEĖeė";
/* the items of this table are 4 bytes long */
static unsigned char ligature[] = "AAÅaaåAEÆaeæccçcCÇDHÐdhðngŋOEŒoeœssßTHÞthþ";

/* latin conversion tables - range 0xa0 .. 0xff */
static unsigned char latin2[] = {
	" Ą˘Ł¤ĽŚ§¨ŠŞŤŹ­ŽŻ°ą˛ł´ľśˇ¸šşťź˝žż"
	"ŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢß"
	"ŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙"
};
static unsigned char latin3[] = {
	" Ħ˘£¤  Ĥ§¨İŞĞĴ­  ݰħ²³´µĥ·¸ışğĵ½  ż"
	"ÀÁÂ  ÄĊĈÇÈÉÊËÌÍÎÏ  ÑÒÓÔĠÖ×ĜÙÚÛÜŬŜß"
	"àáâ  äċĉçèéêëìíîï  ñòóôġö÷ĝùúûüŭŝ˙"
};
static unsigned char latin4[] = {
	" ĄĸŖ¤Ĩϧ¨ŠĒĢŦ­Ž¯°ą˛ŗ´ĩšēģŧŊžŋ"
	"ĀÁÂÃÄÅÆĮČÉĘËĖÍÎĪĐŅŌĶÔÕÖרŲÚÛÜŨŪß"
	"āáâãäåæįčéęëėíîīđņōķôõö÷øųúûüũū˙"
};
static unsigned char latin5[] = {
	" ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿"
	"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏĞÑÒÓÔÕÖרÙÚÛÜİŞß"
	"àáâãäåæçèéêëìíîïğñòóôõö÷øùúûüışÿ"
};
static unsigned char latin6[] = {
	" ĄĒĢĪĨͧĻĐŠŦŽ­ŪŊ°ąēģīĩķ·ļđšŧž―ūŋ"
	"ĀÁÂÃÄÅÆĮČÉĘËĖÍÎÏÐŅŌÓÔÕÖŨØŲÚÛÜÝÞß"
	"āáâãäåæįčéęëėíîïðņōóôõöũøųúûüýþĸ"
};
static unsigned char *latin_tb[5] = {
	latin2, latin3, latin4, latin5, latin6
};

/* add text to the output buffer */
static void txt_add(unsigned char *s, int sz)
{
	if (skip)
		return;
	if (offset + sz > size) {
		size = (offset + sz + 8191) / 8192 * 8192;
		if (!dst)
			dst = malloc(size);
		else
			dst = realloc(dst, size);
		if (!dst) {
			fprintf(stderr, "Out of memory - abort\n");
			exit(EXIT_FAILURE);
		}
	}
	memcpy(dst + offset, s, sz);
	offset += sz;
}

/* add text to the output buffer translating
 * the escape sequences and the non utf-8 characters */
static void txt_add_cnv(unsigned char *s, int sz)
{
	unsigned char *p, c, tmp[4];
	int in_string = 0;

	p = s;
	while (sz > 0) {
		switch (*p) {
		case '"':
			in_string = !in_string;
			break;
		case '%':
			if (in_string)
				break;
			while (--p >= s) {	// start of comment
				if (*p != ' ' && *p != '\t')
					break;
			}
			p++;
			goto done;
		case '\\':
			if (sz >= 4			/* \ooo */
			 && p[1] >= '0' && p[1] <= '3'
			 && p[2] >= '0' && p[2] <= '7'
			 && p[3] >= '0' && p[3] <= '7') {
				c = ((p[1] - '0') << 6)
					+ ((p[2] - '0') << 3)
					+ p[3] - '0';
				if (p != s)
					txt_add(s, (int) (p - s));
				p += 4;
				s = p;
				sz -= 4;
				switch (c) {	/* convert the accidentals */
				case 0x01:
				case 0x81:
					tmp[0] = 0xe2;
					tmp[1] = 0x99;
					tmp[2] = 0xaf;
					txt_add(tmp, 3);
					continue;
				case 0x02:
				case 0x82:
					tmp[0] = 0xe2;
					tmp[1] = 0x99;
					tmp[2] = 0xad;
					txt_add(tmp, 3);
					continue;
				case 0x03:
				case 0x83:
					tmp[0] = 0xe2;
					tmp[1] = 0x99;
					tmp[2] = 0xae;
					txt_add(tmp, 3);
					continue;
				case 0x04:
				case 0x84:
					tmp[0] = 0xf0;
					tmp[1] = 0x9d;
					tmp[2] = 0x84;
					tmp[3] = 0xaa;
					txt_add(tmp, 4);
					continue;
				case 0x05:
				case 0x85:
					tmp[0] = 0xf0;
					tmp[1] = 0x9d;
					tmp[2] = 0x84;
					tmp[3] = 0xab;
					txt_add(tmp, 4);
					continue;
				}
				if (c >= 0x80 && latin > 0)
					goto latin;
				tmp[0] = c;
				txt_add(tmp, 1);
				continue;
			}
			if (sz >= 6		/* \uxxxx */
			 && p[1] == 'u'
			 && isxdigit(p[2])
			 && isxdigit(p[3])
			 && isxdigit(p[4])
			 && isxdigit(p[5])) {
				int i, v;

				v = 0;
				for (i = 2; i < 6; i++) {
					v <<= 4;
					c = p[i];
					if (c <= '9')
						v += c - '0';
					else if (c <= 'F')
						v += c - 'A' + 10;
					else
						v += c - 'a' + 10;
				}
				if (p != s)
					txt_add(s, (int) (p - s));
				p += 6;
				sz -= 6;
				if ((v & 0xdc00) == 0xd800	/* surrogates */
				 && sz >= 6
				 && *p == '\\'
				 && p[1] == 'u'
				 && isxdigit(p[2])
				 && isxdigit(p[3])
				 && isxdigit(p[4])
				 && isxdigit(p[5])) {
					int v2;

					v = (v - 0xd7c0) << 10;
					v2 = 0;
					for (i = 2; i < 6; i++) {
						v2 <<= 4;
						c = p[i];
						if (c <= '9')
							v2 += c - '0';
						else if (c <= 'F')
							v2 += c - 'A' + 10;
						else
							v2 += c - 'a' + 10;
					}
					v2 -= 0xdc00;
					v += v2;
					p += 6;
					sz -= 6;
				}
//fixme: else error
				s = p;
				if (v < 0x80) {	/* convert to UTF-8 */
					tmp[0] = v;
					i = 1;
				} else if (v < 0x800) {
					tmp[0] = 0xc0 | (v >> 6);
					tmp[1] = 0x80 | (v & 0x3f);
					i = 2;
				} else if (v < 0x10000) {
					tmp[0] = 0xe0 | (v >> 12);
					tmp[1] = 0x80 | ((v >> 6) & 0x3f);
					tmp[2] = 0x80 | (v & 0x3f);
					i = 3;
				} else {
					tmp[0] = 0xf0 | (v >> 18);
					tmp[1] = 0x80 | ((v >> 12) & 0x3f);
					tmp[2] = 0x80 | ((v >> 6) & 0x3f);
					tmp[3] = 0x80 | (v & 0x3f);
					i = 4;
				}
				txt_add(tmp, i);
				continue;
			}
			if (sz >= 3) {
				unsigned char *q;

				switch (p[1]) {
				case '`': q = grave; break;
				case '\'': q = acute; break;
				case '^': q = circumflex; break;
				case ',': q = cedilla; break;
				case '"': q = umlaut; break;
				case '~': q = tilde; break;
				case 'o': q = ring; break;
				case '=': q = macron; break;
				case '/': q = slash; break;
				case ';': q = ogonek; break;
				case 'v': q = caron; break;
				case 'u': q = breve; break;
				case 'H':
				case ':': q = hungumlaut; break;
				case '.': q = dot; break;
				default:
					q = ligature;
					do {
						if (*q == p[1]
						 && q[1] == p[2])
							break;
						q += 4;
					} while (*q != '\0');
					if (*q != '\0') {
						if (p != s)
							txt_add(s, (int) (p - s));
						txt_add(q + 2, 2);
						p += 3;
						sz -= 3;
						s = p;
						continue;
					}
					q = 0;
					break;
				}
				if (q != 0) {
					do {
						if (*q == p[2])
							break;
						q += 3;
					} while (*q != '\0');
					if (*q != '\0') {
						if (p != s)
							txt_add(s, (int) (p - s));
						txt_add(q + 1, 2);
						p += 3;
						sz -= 3;
						s = p;
						continue;
					}
				}
			}
			p++;
			sz--;
			if (sz > 0) {
				p++;
				sz--;
			}
			continue;
		}
		if (*p >= 0x80 && latin > 0) {
			if (p != s)
				txt_add(s, (int) (p - s));
			c = *p++;
			s = p;
			sz--;
latin:
			if (c < 0xa0 || latin == 1) {
				tmp[0] = 0xc0 | ((c >> 6) & 0x03);
				tmp[1] = 0x80 | (c & 0x3f);
				txt_add(tmp, 2);
			} else {
				unsigned char *q;

				q = latin_tb[latin - 2];
				txt_add(q + (c - 0xa0) * 2, 2);
			}
			continue;
		}
		p++;
		sz--;
	}
done:
	if (p != s)
		txt_add(s, (int) (p - s));
}

static void txt_add_eos(char *fname, int linenum)
{
	static unsigned char eos = '\0';

	/* special case for continuation lines in ABC version 2.0 */
	if (parse.abc_vers == (2 << 16)
	 && offset > 0
	 && dst[offset - 1] == '\\') {
		offset--;
		return;
	}
	txt_add(&eos, 1);
	abc_parse((char *) dst, fname, linenum);
	offset = 0;
}

/* get the ABC version */
static void get_vers(char *p)
{
	int i, j, k;

	i = j = k = 0;
	if (sscanf(p, "%d.%d.%d", &i, &j, &k) != 3)
		if (sscanf(p, "%d.%d", &i, &j) != 2)
			sscanf(p, "%d", &i);
	parse.abc_vers = (i << 16) + (j << 8) + k;
}

/* check if the current tune is to be selected */
static int tune_select(unsigned char *s)
{
	struct slre slre;
	unsigned char *p, *sel;

	/* if there is a list of tune indexes,
	 * check the tune index */
	sel = selection;
	if (isdigit(*sel)) {
		int tune_number, cur_sel, end_sel, n;

		/* get the tune number ('s' points to X:) */
		tune_number = strtod((char *) s + 2, 0);

		/* search it in the number list */
		for (;;) {
			if (sscanf((char *) sel, "%d%n", &cur_sel, &n) != 1)
				break;
			sel += n;
			if (*sel == '-') {
				sel++;
				if (sscanf((char *) sel, "%d%n", &end_sel, &n) != 1)
					end_sel = ~0u >> 1;
				else
					sel += n;
			} else {
				end_sel = cur_sel;
			}
			if (tune_number >= cur_sel && tune_number <= end_sel)
				return 1;
			if (*sel != ',')
				break;
			sel++;
		}
		if (*sel == '\0')
			return 0;
	}

	for (p = s + 2; ; p++) {
		switch (*p) {
		case '\0':
			return 0;
		default:
			continue;
		case '\n':
		case '\r':
			break;
		}
		if (p[1] != 'K' || p[2] != ':')
			continue;
		p += 3;
		while (*p != '\n' && *p != '\r' && *p != '\0')
			p++;
		if (*p != '\0')
			p++;		/* keep the EOL for RE with '\s' */
		break;
	}
//fixme: should compile only one time
	if (!slre_compile(&slre, (char *) sel))
		return 0;
	return slre_match(&slre, (char *) s, p - s, 0);
}

/* -- front end parser -- */
void frontend(unsigned char *s,
		int ftype,
		char *fname,
		int linenum)
{
	unsigned char *p, *q, c, *begin_end, sep;
	int i, l, state, str_cnv_p, histo, end_len;
	char prefix_sav[4];
	int latin_sav = 0;		/* have C compiler happy */

	begin_end = NULL;
	end_len = 0;
	histo = 0;
	state = 0;

	if (ftype == FE_ABC
	 && strncmp((char *) s, "%abc-", 5) == 0) {
		get_vers((char *) s + 5);
		while (*s != '\0'
		    && *s != '\r'
		    && *s != '\n')
			s++;
		if (*s != '\0') {
			s++;
			if (s[-1] == '\r' && *s == '\n')
				s++;
		}
		linenum++;
	}

	/* if unknown encoding, check if latin1 or utf-8 */
	if (ftype == FE_ABC
	 && parse.abc_vers >= ((2 << 16) | (1 << 8))) {	// if ABC version >= 2.1
		latin = 0;				// always UTF-8
	} else {
		for (p = s; *p != '\0'; p++) {
			c = *p;
			if (c == '\\') {
				if (!isdigit(p[1]))
					continue;
				if ((p[1] == '0' || p[1] == '2')
				 && p[2] == '0')	/* accidental */
					continue;
				latin = 1;
				break;
			}
			if (c < 0x80)
				continue;
			if (c >= 0xc2) {
				if ((p[1] & 0xc0) == 0x80) {
					latin = 0;
					break;
				}
			}
			latin = 1;
			break;
		}
	}
	latin_sav = latin;		/* (have gcc happy) */

	/* scan the file */
	skip = 0;
	while (*s != '\0') {

		/* get a line */
		str_cnv_p = 0;
		p = s;
		while (*p != '\0'
		    && *p != '\r'
		    && *p != '\n') {
			if (*p == '\\'
			 || *p == '%'
			 || (latin > 0 && *p >= 0x80))
				str_cnv_p = 1;
			p++;
		}
		l = p - s;
		if (*p != '\0') {
			p++;
			if (p[-1] == '\r' && *p == '\n')	/* (DOS) */
				p++;
		}
		linenum++;

		if (skip) {
			if (l != 0)
				goto ignore;
			skip = 0;
		}
		if (begin_end) {
			if (ftype == FE_FMT) {
				if (strncmp((char *) s, "end", 3) == 0
				 && strncmp((char *) s + 3,
						(char *) begin_end, end_len) == 0) {
					begin_end = NULL;
					goto next_eol;
				}
				if (*s == '%')
					goto ignore;		/* comment */
				goto next;
			}
			if (*s == '%' && strchr(prefix, s[1])) {
				q = s + 2;
				while (*q == ' ' || *q == '\t')
					q++;
				if (strncmp((char *) q, "end", 3) == 0
				 && strncmp((char *) q + 3,
						(char *) begin_end, end_len) == 0) {
					begin_end = NULL;
					goto next_eol;
				}
			}
			if (strncmp("ps", (char *) begin_end, end_len) == 0) {
				if (*s == '%')
					goto ignore;		/* comment */
			} else {
				if (*s == '%' && strchr(prefix, s[1])) {
					s += 2;
					l -= 2;
				}
			}
			goto next;
		}

		while (l > 0 && isspace(s[l - 1]))
			l--;

		if (l == 0) {			/* empty line */
			switch (state) {
			default:
				goto ignore;
			case 1:
				fprintf(stderr,
					"Line %d: Empty line in tune header - K:C added\n",
					linenum);
				txt_add((unsigned char *) "K:C", 3);
				txt_add_eos(fname, linenum);
				/* fall thru */
			case 2:
				state = 0;
				strcpy(prefix, prefix_sav);
				latin = latin_sav;
				break;
			}
			goto next_eol;
		}
		if (histo) {			/* H: continuation */
			if ((s[1] == ':'
			  && (isalpha(*s) || *s == '+'))
			 || (*s == '%' && strchr(prefix, s[1]))) {
				histo = 0;
			} else {
				txt_add((unsigned char *) "H:", 2);
				goto next;
			}
		}

		/* special case 'space* "%" ' */
		if (*s == ' ' || *s == '\t') {
			q = s;
			do {
				q++;
			} while (*q == ' ' || *q == '\t');
			if (*q == '%')
				goto ignore;
		}

		if (ftype == FE_PS) {
			if (*s == '%')
				goto ignore;
			goto next;
		}

		/* treat the pseudo-comments */
		if (ftype == FE_FMT) {
			if (*s == '%')
				goto ignore;
			goto pscom;
		}
		if (*s == 'I' && s[1] == ':') {
			s += 2;
			l -= 2;
			while (*s == ' ' || *s == '\t') {
				s++;
				l--;
			}
			txt_add((unsigned char *) "%%", 2);
			goto info;
		}
		if (*s == '%') {
			if (!strchr(prefix, s[1]))	/* pure comment */
				goto ignore;
			s += 2;
			l -= 2;
			if (strncmp((char *) s, "abcm2ps ", 8) == 0) {
				s += 8;
				l -= 8;
				while (*s == ' ' || *s == '\t') {
					s++;
					l--;
				}
				for (i = 0; i < sizeof prefix - 1; i++) {
					if (*s == ' ' || *s == '\t'
					 || --l < 0)
						break;
					prefix[i] = *s++;
				}
				if (i == 0)
					prefix[i++] = '%';
				prefix[i] = '\0';
				goto ignore;
			}
			if (strncmp((char *) s, "abc-version ", 12) == 0) {
				get_vers((char *) s + 12);
				goto ignore;
			}
pscom:
			while (*s == ' ' || *s == '\t') {
				s++;
				l--;
			}
			txt_add((unsigned char *) "%%", 2);
			if (strncmp((char *) s, "begin", 5) == 0) {
				q = begin_end = s + 5;
				while (!isspace(*q))
					q++;
				end_len = q - begin_end;
				goto next;
			}
info:
			if (strncmp((char *) s, "encoding ", 9) == 0
			 || strncmp((char *) s, "abc-charset ", 12) == 0) {
				if (*s == 'e')
					q = s + 9;
				else
					q = s + 12;
				while (*q == ' ' || *q == '\t')
					q++;
				if (strncasecmp((char *) q, "latin", 5) == 0) {
					q += 5;
				} else if (strncasecmp((char *) q, "iso-8859-", 9) == 0) {
					q += 9;
				} else if (strncasecmp((char *) q, "utf-8", 5) == 0
					|| strncasecmp((char *) q, "native", 6) == 0) {
					latin = 0;
					goto next;
				} else if (!isdigit(*q)) {
					goto next;	/* unknown charset */
				}
				switch (*q) {
				case '1':
					if (q[1] == '0')
						latin = 6;
					else
						latin = 1;
					break;
				case '2': latin = 2; break;
				case '3': latin = 3; break;
				case '4': latin = 4; break;
				case '5':
					if (q[-1] != '-')
						latin = 5;
					break;
				case '6':
					if (q[-1] != '-')
						latin = 6;
					break;
/*fixme: iso-8859 5..8 not treated */
				case '9': latin = 5; break;
				}
				goto next;
			}
			if (strncmp((char *) s, "format ", 7) == 0
			  || strncmp((char *) s, "abc-include ", 12) == 0) {
				int skip_sav;

				if (*s == 'f')
					s += 7;
				else
					s += 12;
				while (*s == ' ' || *s == '\t')
					s++;
				q = s;
				while (*q != '\0'
				    && *q != '%'
				    && *q != '\n'
				    && *q != '\r')
					q++;
				while (q[-1] == ' ')
					q--;
				sep = *q;
				*q = '\0';
				skip_sav = skip;
				offset = 0;
				include_file(s);
				skip = skip_sav;
				*q = sep;
				goto ignore;
			}
			if (strncmp((char *) s, "select", 6) == 0) {
				s += 6;
				if (*s == '\n') {	/* select clear */
					q = s;
				} else if (*s != ' ' && *s != '\t') {
					goto next;
				} else {
					while (*s == ' ' || *s == '\t')
						s++;
					q = s;
					while (*q != '\0'
					    && *q != '%'
					    && *q != '\n'
					    && *q != '\r')
						q++;
					while (q[-1] == ' ' || q[-1] == '\t')
						q--;
					if (strncmp((char *) q - 5, " lock", 5) == 0)
						q -= 5;
				}
				if (selection) {
					free(selection);
					selection = NULL;
				}
				if (q != s) {
					sep = *q;
					*q = '\0';
					selection = (unsigned char *) strdup((char *) s);
					*q = sep;
				}
				offset = 0;
				goto ignore;
			}
			goto next;
		}

		/* treat the information fields */
		if (s[1] == ':' && (isalpha(*s) || *s == '+')) {
			c = *s;
			switch (c) {
			case 'I':		/* treat as a pseudo-comment */
				s += 2;
				l -= 2;
				goto pscom;
			case 'X':
				switch (state) {
				case 1:
					fprintf(stderr,
						"Line %d: X: found in tune header - K:C added\n",
						linenum);
					txt_add((unsigned char *) "K:C", 3);
					txt_add_eos(fname, linenum);
					txt_add_eos(fname, linenum);	/* empty line */
					break;
				case 2:
					txt_add_eos(fname, linenum);	/* no empty line - minor error */
					break;
				}
				if (selection) {
					skip = !tune_select(s);
					if (skip)
						goto ignore;
				}
				state = 1;
				strcpy(prefix_sav, prefix);
				latin_sav = latin;
				break;
			case 'U':
				break;
			case 'H':
				histo = 1;
				break;
			default:
				if (state == 0			/* if global */
				 && strchr("dKPQsVWw", *s) != NULL)
					goto ignore;
				if (*s == 'K')
					state = 2;
				break;
			}
			txt_add(s, 2);
			s += 2;
			l -= 2;
			while (*s == ' ' || *s == '\t') {
				s++;
				l--;
			}
			str_cnv_p = 1;
			goto next;
		}

		/* treat the music lines */
		if (state == 0)				/* if not in tune */
			goto ignore;
#if 0
// what was this used for?
		if (!str_cnv_p)
			goto next;
		str_cnv_p = 0;
		for (i = 0; i < l; i++) {
			if (s[i] != '"')
				continue;
			i++;
			txt_add(s, i);
			s += i;
			l -= i;
			for (i = 0; i < l; i++) {
				if (s[i] == '"' && s[i - 1] != '\\')
					break;
			}
//fixme: if i == l, no end of string - error
			txt_add_cnv(s, i);
			s += i;
			l -= i;
			i = 0;
		}
#endif
next:
		if (str_cnv_p)
			txt_add_cnv(s, l);
		else
			txt_add(s, l);
		if (begin_end)
			txt_add((unsigned char *) "\n", 1);
		else
next_eol:
			txt_add_eos(fname, linenum);
ignore:
		s = p;
	}
	if (state == 1)
		fprintf(stderr,
			"Line %d: Unexpected EOF in header definition\n",
			linenum);
	if (begin_end)
		fprintf(stderr,
			"Line %d: No %%%%end after %%%%begin\n",
			linenum);
	abc_eof();
}
abcm2ps-8.5.2/glyph.c0000644000175000017500000002012712451551234012442 0ustar  jefjef/*
 * utf-8 to glyph translation
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 2011-2015 Jean-François Moine (http://moinejf.free.fr)
 *
 * 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.
 */

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

#include "abc2ps.h"

static char *c2[64] = {
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	"nbspace",	"exclamdown",	"cent",		"sterling",
	"currency",	"yen",		"brokenbar",	"section",
	"dieresis",	"copyright",	"ordfeminine",	"guillemotleft",
	"logicalnot",	"sfthyphen",	"registered",	"macron",
	"degree",	"plusminus",	"twosuperior",	"threesuperior",
	"acute",	"mu",		"paragraph",	"periodcentered",
	"cedilla",	"onesuperior",	"ordmasculine",	"guillemotright",
	"onequarter",	"onehalf",	"threequarters","questiondown"
};
static char *c3[64] = {
	"Agrave",	"Aacute",	"Acircumflex",	"Atilde",
	"Adieresis",	"Aring",	"AE",		"Ccedilla",
	"Egrave",	"Eacute",	"Ecircumflex",	"Edieresis",
	"Igrave",	"Iacute",	"Icircumflex",	"Idieresis",
	"Eth",		"Ntilde",	"Ograve",	"Oacute",
	"Ocircumflex",	"Otilde",	"Odieresis",	"multiply",
	"Oslash",	"Ugrave",	"Uacute",	"Ucircumflex",
	"Udieresis",	"Yacute",	"Thorn",	"germandbls",
	"agrave",	"aacute",	"acircumflex",	"atilde",
	"adieresis",	"aring",	"ae",		"ccedilla",
	"egrave",	"eacute",	"ecircumflex",	"edieresis",
	"igrave",	"iacute",	"icircumflex",	"idieresis",
	"eth",		"ntilde",	"ograve",	"oacute",
	"ocircumflex",	"otilde",	"odieresis",	"divide",
	"oslash",	"ugrave",	"uacute",	"ucircumflex",
	"udieresis",	"yacute",	"thorn",	"ydieresis"
};
static char *c4[64] = {
	"Amacron",	"amacron",	"Abreve",	"abreve",
	"Aogonek",	"aogonek",	"Cacute",	"cacute",
	"Ccircumflex",	"ccircumflex",	"Cdotaccent",	"cdotaccent",
	"Ccaron",	"ccaron",	"Dcaron",	"dcaron",
	"Dcroat",	"dcroat",	"Emacron",	"emacron",
	"Ebreve",	"ebreve",	"Edotaccent",	"edotaccent",
	"Eogonek",	"eogonek",	"Ecaron",	"ecaron",
	"Gcircumflex",	"gcircumflex",	"Gbreve",	"gbreve",
	"Gdotaccent",	"gdotaccent",	"Gcommaaccent",	"gcommaaccent",
	"Hcircumflex",	"hcircumflex",	"Hbar",		"hbar",
	"Itilde",	"itilde",	"Imacron",	"imacron",
	"Ibreve",	"ibreve",	"Iogonek",	"iogonek",
	"Idotaccent",	"dotlessi",	"IJ",		"ij",
	"Jcircumflex",	"jcircumflex",	"Kcedilla",	"kcedilla",
	"kgreenlandic",	"Lacute",	"lacute",	"Lcedilla",
	"lcedilla",	"Lcaron",	"lcaron",	"Ldot"
};
static char *c5[64] = {
	"ldot",		"Lslash",	"lslash",	"Nacute",
	"nacute",	"Ncedilla",	"ncedilla",	"tmacron",
	"ncaron",	"napostrophe",	"Eng",		"eng",
	"Omacron",	"omacron",	"Obreve",	"obreve",
	"Ohungarumlaut","ohungarumlaut","OE",		"oe",
	"Racute",	"racute",	"Rcommaaccent",	"rcommaaccent",
	"Rcaron",	"rcaron",	"Sacute",	"sacute",
	"Scircumflex",	"scircumflex",	"Scedilla",	"scedilla",
	"Scaron",	"scaron",	"Tcedilla",	"tcedilla",
	"Tcaron",	"tcaron",	"Tbar",		"tbar",
	"Utilde",	"utilde",	"Umacron",	"umacron",
	"Ubreve",	"ubreve",	"Uring",	"uring",
	"Uhungarumlaut","uhungarumlaut","Uogonek",	"uogonek",
	"Wcircumflex",	"wcircumflex",	"Ycircumflex",	"ycircumflex",
	"Ydieresis",	"Zacute",	"zacute",	"Zdotaccent",
	"zdotaccent",	"Zcaron",	"zcaron",	"longs"
};

static char *ce[64] = {
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, "Delta", NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

static char *e299[64] = {
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
//	NULL, NULL, NULL, NULL, NULL, "uni266D", "uni266E", "uni266F",
	NULL, NULL, NULL, NULL, NULL, "flat", "natural", "sharp",
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
static char **e2[64] = {
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, e299, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

static char *f09d84[64] = {
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
//	NULL, NULL, "u1D12A", "u1D12B",
	NULL, NULL, "double_sharp", "double_flat",
					NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
static char **f09d[64] = {
	NULL, NULL, NULL, NULL, f09d84, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
static char ***f0[64] = {
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, f09d, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

/* 1st character - c2..ff */
static char **utf_1[62] = {
			c2,	c3,	c4,	c5,	NULL,	NULL,
	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	ce,	NULL,
	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
	NULL,	NULL,	(char **) e2, NULL,
					NULL,	NULL,	NULL,	NULL,
	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
	(char **) f0, NULL, NULL, NULL,	NULL,	NULL,	NULL,	NULL,
	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
};

/* output the glyph of a utf-8 character */
/* return the next character */
char *glyph_out(char *p)
{
	int i1, i2, i3, i4;
	char **g, *q;

	g = NULL;
	i1 = (unsigned char) *p++ - 0xc2;
	i2 = (unsigned char) *p++ - 0x80;
	if (i1 >= 0xe0 - 0xc2) {
		i3 = (unsigned char) *p++ - 0x80;
		if (i1 >= 0xf0 - 0xc2)
			i4 = (unsigned char) *p++ - 0x80;
		else
			i4 = -1;
	} else {
		i3 = -1;
		i4 = -1;
	}
	if (i1 >= 0 && i2 >= 0) {
		g = (char **) utf_1[i1];
		if (g) {
			g = (char **) g[i2];
			if (i3 >= 0 && g) {
				g = (char **) g[i3];
				if (i4 >= 0 && g)
					g = (char **) g[i4];
			}
		}
		q = (char *) g;
	} else {
		q = NULL;
	}
	if (!q)
		q = ".notdef";
	a2b("/%s", q);
	return p;
}

/* -- add a glyph -- */
/* %%glyph hex_value glyph_name */
void glyph_add(char *p)
{
	int val, i1, i2, i3, i4;
	char **g, **g1, *q;

	val = strtoul(p, &q, 16);	/* unicode value */
	if (val < 0x80
	 || val >= 0x100000) {
		 error(1, 0, "Bad unicode value '%s'", p);
		 return;
	}
	p = q;
	while (isspace(*p))
		p++;
	i3 = i4 = -1;
	if (val < 0x0400) {
		i1 = (val >> 6) - 2;
		i2 = val & 0x3f;
	} else if (val < 0x10000) {
		i1 = (val >> 12) + 0x20 - 2;
		i2 = (val >> 6) & 0x3f;
		i3 = val & 0x3f;
	} else {
		i1 = (val >> 18) + 0x30 - 2;
		i2 = (val >> 12) & 0x3f;
		i3 = (val >> 6) & 0x3f;
		i4 = val & 0x3f;
	}
	g1 = utf_1[i1];
	if (!g1) {
		g1 = calloc(64, sizeof(char **));
		utf_1[i1] = g1;
	}
	if (i3 < 0) {
		g1[i2] = strdup(p);
		return;
	}
	g = (char **) g1[i2];
	if (!g) {
		g = calloc(64, sizeof(char **));
		g1[i2] = (char *) g;
	}
	if (i4 < 0) {
		g[i3] = strdup(p);
		return;
	}
	g1 = (char **) g[i3];
	if (!g1) {
		g1 = calloc(64, sizeof(char **));
		g[i3] = (char *) g1;
	}
	g1[i4] = strdup(p);
}
abcm2ps-8.5.2/glyphs.abc0000644000175000017500000000426112452443771013140 0ustar  jefjef% Definition of the glyph names of some musical unicode characters
% for use in PostScript output (without pango) with abcm2ps.
% These values were extracted from FreeSerif.ttf.

%					% character value in string
%%glyph	 2669 quarternote		% \u2669
%%glyph	 266a musicalnote		% \u266a
%%glyph	 266b musicalnotedbl		% \u266b
%%glyph	 266c beamedsixteenthnotes	% \u266c
%%glyph	 266d flat			% \u266d
%%glyph	 266e natural			% \u266e
%%glyph	 266f sharp			% \u266f
%%glyph 1d109 dal_segno			% \ud834\udd09
%%glyph 1d10a dal_capo			% \ud834\udd0a
%%glyph 1d10b segno			% \ud834\udd0b
%%glyph 1d10c coda			% \ud834\udd0c
%%glyph 1d110 fermata			% \ud834\udd10
%%glyph 1d112 breath_mark		% \ud834\udd12
%%glyph 1d113 caesura			% \ud834\udd13
%%glyph 1d114 brace			% \ud834\udd14
%%glyph 1d115 bracket			% \ud834\udd15
%%glyph 1d11e g_clef			% \ud834\udd1e
%%glyph 1d11f g_clef_ottava_alta	% \ud834\udd1f
%%glyph 1d120 g_clef_ottava_bassa	% \ud834\udd20
%%glyph 1d121 c_clef			% \ud834\udd21
%%glyph 1d122 f_clef			% \ud834\udd22
%%glyph 1d123 f_clef_ottava_alta	% \ud834\udd23
%%glyph 1d124 f_clef_ottava_bassa	% \ud834\udd24
%%glyph 1d125 drum_clef_1		% \ud834\udd25
%%glyph 1d126 drum_clef_2		% \ud834\udd26
%%glyph 1d12a double_sharp		% \ud834\udd2a
%%glyph 1d12b double_flat		% \ud834\udd2b
%%glyph 1d134 common_time		% \ud834\udd34
%%glyph 1d135 cut_time			% \ud834\udd35
%%glyph 1d136 ottava_alta		% \ud834\udd36
%%glyph 1d137 ottava_bassa		% \ud834\udd37
%%glyph 1d13d quarter_rest		% \ud834\udd3d
%%glyph 1d15d whole_note		% \ud834\udd5d
%%glyph 1d15e half_note			% \ud834\udd5e
%%glyph 1d15f quarter_note		% \ud834\udd5f
%%glyph 1d160 eighth_note		% \ud834\udd60
%%glyph 1d18d subito			% \ud834\udd8d
%%glyph 1d18e z_mus			% \ud834\udd8e
%%glyph 1d18f piano			% \ud834\udd8f
%%glyph 1d190 mezzo			% \ud834\udd90
%%glyph 1d191 forte			% \ud834\udd91
%%glyph 1d19b ornament_stroke_1		% \ud834\udd9b
%%glyph 1d19c ornament_stroke_2		% \ud834\udd9c
%%glyph 1d19d ornament_stroke_3		% \ud834\udd9d
%%glyph 1d19e ornament_stroke_4		% \ud834\udd9e
%%glyph 1d1a0 ornament_stroke_6		% \ud834\udda0
%%glyph 1d1a3 ornament_stroke_9		% \ud834\udda9
%%glyph 1d1ae pedal_mark		% \ud834\uddae
%%glyph 1d1af pedal_up_mark		% \ud834\uddaf
abcm2ps-8.5.2/landscape.fmt0000644000175000017500000000022307337660472013624 0ustar  jefjef
% format for wide output in landscape mode

   landscape 1

   titleleft  yes
   titlefont Palatino-Bold 22
   composerspace 0.6cm
   staffsep 60
abcm2ps-8.5.2/music.c0000644000175000017500000032453512461130744012451 0ustar  jefjef/*
 * Music generator.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2015 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.
 */

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

#include "abc2ps.h"

struct STAFF_S staff_tb[MAXSTAFF];	/* staff table */
struct SYMBOL *tsnext;		/* next line when cut */
float realwidth;		/* real staff width while generating */

static int insert_meter;	/* insert time signature (1) and indent 1st line (2) */
static float beta_last;		/* for last short short line.. */

#define AT_LEAST(a,b)  do { float tmp = b; if(a<tmp) a=tmp; } while (0)

/* width of notes indexed by log2(note_length) */
float space_tb[NFLAGS_SZ] = {
	7, 10, 14.15, 20, 28.3,
	40,				/* crotchet */
	56.6, 80, 113, 150
};
static int smallest_duration;

/* upper and lower space needed by rests */
static struct {
	char u, l;
} rest_sp[NFLAGS_SZ] = {
	{16, 31},
	{16, 25},
	{16, 19},
	{10, 19},
	{10, 13},
	{10, 13},			/* crotchet */
	{7, 7},
	{10, 4},
	{10, 7},
	{10, 13}
};

/* -- decide whether to shift heads to other side of stem on chords -- */
/* also position accidentals to avoid too much overlap */
/* this routine is called only once per tune */
static void set_head_directions(struct SYMBOL *s)
{
	int i, i1, i2, n, sig, d, shift;
//, i3
	int p1, ps;
//, p2, p3, m, nac
	float dx, dx1, dx_max, dx_head;
//, dx2, dx3, shmin, shmax
//	unsigned char ax_tb[MAXHD], ac_tb[MAXHD];
	static float dx_tb[4] = {
		9, 10, 12, 15.3
	};
	/* distance for no overlap - index: [prev acc][cur acc] */
//	static char dt_tb[4][4] = {
//		{5, 5, 5, 5},		/* dble sharp */
//		{5, 6, 6, 6},		/* sharp */
//		{5, 6, 5, 6},		/* natural */
//		{5, 5, 5, 5}		/* flat */
//	};

	/* special case when single note */
	dx_head = dx_tb[s->head];
	if (s->dur >= BASE_LEN * 2 && s->head == H_OVAL)
		dx_head = 15.8;
	n = s->nhd;
	if (n == 0) {
		if (s->as.u.note.accs[0] != 0) {
			if (s->as.flags & ABC_F_GRACE)
				dx_head *= 0.7;
			s->shac[0] = dx_head;
		}
		return;
	}

	/* set the head shifts */
	dx = dx_head * 0.78;
	if (s->as.flags & ABC_F_GRACE)
		dx *= 0.5;
	sig = s->stem;
	if (sig >= 0) {
		i1 = 1;
		i2 = n + 1;
		ps = s->pits[0];
	} else {
		dx = -dx;
		i1 = n - 1;
		i2 = -1;
		ps = s->pits[n];
	}
	shift = 0;
	dx_max = 0;
	for (i = i1; i != i2; i += sig) {
		d = s->pits[i] - ps;
		ps = s->pits[i];
		if (d == 0) {
			if (shift) {		/* unison on shifted note */
				float new_dx = s->shhd[i] = s->shhd[i - sig] + dx;
				s->shac[i] = -new_dx;
				if (dx_max < new_dx)
					dx_max = new_dx;
				continue;
			}
			if (i + sig != i2	/* second after unison */
//fixme: should handle many unisons after second
			 && ps + sig == s->pits[i + sig]) {
				s->shhd[i] = -dx;
				s->shac[i] = dx;
				if (dx_max < -dx)
					dx_max = -dx;
				continue;
			}
		}
		if (d < 0)
			d = -d;
		if (d > 3 || (d >= 2 && s->head < H_SQUARE)) {
			shift = 0;
		} else {
			shift = !shift;
			if (shift) {
				s->shhd[i] = dx;
				s->shac[i] = -dx;
				if (dx_max < dx)
					dx_max = dx;
			}
		}
	}
	s->xmx = dx_max;				/* shift the dots */

	/* set the accidental shifts */
#if 1
	for (i = n; i >= 0; i--) {			// from top to bottom
		unsigned char acc = s->as.u.note.accs[i];
		if (!acc)
			continue;
		dx = -s->shhd[i] + dx_head;
		ps = s->pits[i];
		for (i1 = 0; i1 <= n; i1++) {
			p1 = s->pits[i1];
			if (p1 < ps - 3 || p1 > ps + 3)	// pitch far enough
				continue;
			dx1 = -s->shhd[i1] + dx_head;
			if (dx < dx1)
				dx = dx1;
			if (p1 >= ps + 2
			 && dx < s->shac[i1] - 6)
				continue;
			if (dx < s->shac[i1] + 7)
				dx = s->shac[i1] + 7;
		}
		s->shac[i] = dx;
	}
#else
	nac = 0;
	for (i = n; i >= 0; i--) {	/* from top to bottom */
		if ((i1 = s->as.u.note.accs[i]) != 0) {
			ax_tb[nac++] = i;
			if (i1 & 0xf8)
				i1 = A_SH;	/* micro-tone same as sharp */
			else if (i1 == A_DF)
				i1 = A_FT;	/* dble flat same as flat */
			else if (i1 == A_DS)
				i1 = 0;		/* (max -> 0) */
			ac_tb[i] = i1;
		}
	}
	if (nac == 0)			/* no accidental */
		return;
	dx = dx_tb[s->head];
	m = n;
	p2 = i2 = 0;			/* (compiler warning) */
	dx2 = 0;
	ps = 255;
	for (i = 0; i < nac; i++) {
		i1 = ax_tb[i];
		p1 = s->pits[i1];
		if (m >= 0) {		/* see if any head shift */
			if (ps - s->pits[i1] >= 4) {
//				for (m--; m >= 0; m--) {
				while (--m >= 0) {
					if (s->shhd[m] < 0) {
						ps = s->pits[m];
						break;
					}
				}
			}
		}
		dx1 = dx;
		if (m >= 0 && s->shhd[m] < 0
		 && ps - p1 < 4 && ps - p1 > -4)
			dx1 -= s->shhd[m];
		if (s->as.flags & ABC_F_GRACE)
			dx1 *= 0.7;
		if (i == 0) {	/* no other shift for the 1st accidental */
			s->shac[i1] = dx1;
			i2 = i1;
			p2 = p1;
			dx2 = dx1;
			continue;
		}
		d = dt_tb[ac_tb[i2]][ac_tb[i1]];
		if (p2 - p1 < d) {		/* if possible overlap */
			if (s->as.u.note.accs[i1] & 0xf8) { /* microtonal */
				shmin = 6.5;
				shmax = 9;
			} else {
				shmin = 4.5;
				shmax = 7;
			}
			if (s->as.flags & ABC_F_GRACE) {
				shmin *= 0.7;
				shmax *= 0.7;
			}
			if (i >= 2) {
				i3 = ax_tb[i - 2];
				p3 = s->pits[i3];
				d = dt_tb[ac_tb[i3]][ac_tb[i1]];
				if (p3 - p1 < d) {
					dx3 = s->shac[i3];
					if (p3 - p1 >= 4
					 && (ac_tb[i3] != A_SH || ac_tb[i1] != A_SH)) {
						if (dx1 > dx3 - shmin && dx1
									< dx3 + shmin)
							dx1 = dx3 + shmin;
					} else {
						if (dx1 > dx3 - shmax && dx1
									< dx3 + shmax)
							dx1 = dx3 + shmax;
					}
				}
			}
			if (p2 - p1 >= 4
			 && (ac_tb[i2] != A_SH || ac_tb[i1] != A_SH)) {
				if (dx1 > dx2 - shmin && dx1 < dx2 + shmin) {
					if (dx1 + shmin < dx2 + shmin)
						s->shac[i2] = dx1 + shmin;
					else
						dx1 = dx2 + shmin;
				}
			} else {
				if (dx1 > dx2 - shmax && dx1 < dx2 + shmax) {
					if (dx1 + shmax < dx2 + shmax)
						s->shac[i2] = dx1 + shmax;
					else
						dx1 = dx2 + shmax;
				}
			}
		}
		s->shac[i1] = dx1;
		i2 = i1;
		p2 = p1;
		dx2 = dx1;
	}
#endif
}

/* -- unlink a symbol -- */
void unlksym(struct SYMBOL *s)
{
	if (!s->next) {
		if (s->extra) {
			s->type = FMTCHG;
			s->u = -1;
			return;
		}
	} else {
		s->next->prev = s->prev;
//		if (s->type == NOTEREST
//		 && !(s->sflags & S_BEAM_END))
//			s->next->sflags |= S_BEAM_ST;
		if (s->extra) {
			struct SYMBOL *g;

			g = s->next->extra;
			if (!g) {
				s->next->extra = s->extra;
			} else {
				for (; g->next; g = g->next)
					;
				g->next = s->extra;
			}
		}
	}
	if (s->prev)
		s->prev->next = s->next;
	else
		voice_tb[s->voice].sym = s->next;
	if (s->ts_next) {
		if ((s->sflags & S_SEQST)
		 && !(s->ts_next->sflags & S_SEQST)) {
			s->ts_next->sflags |= S_SEQST;
			s->ts_next->shrink = s->shrink;
			s->ts_next->space = s->space;
		}
		if (s->sflags & S_NEW_SY)
			s->ts_next->sflags |= S_NEW_SY;
		s->ts_next->ts_prev = s->ts_prev;
	}
	if (s->ts_prev)
		s->ts_prev->ts_next = s->ts_next;
	if (tsfirst == s)
		tsfirst = s->ts_next;
	if (tsnext == s)
		tsnext = s->ts_next;
}

/* -- check if voice combine may occur -- */
static int may_combine(struct SYMBOL *s)
{
	struct SYMBOL *s2;
	int nhd2;

	s2 = s->ts_next;
	if (!s2 || s2->type != NOTEREST)
		return 0;
	if (s2->voice == s->voice
	 || s2->staff != s->staff
	 || s2->time != s->time
	 || s2->dur != s->dur)
		return 0;
	if (cfmt.combinevoices <= 0
	 && s2->as.type != s->as.type)
		return 0;
	if (s->as.u.note.dc.n + s2->as.u.note.dc.n >= MAXDC)
		return 0;
//fixme: should check the double decorations
	if (s->gch && s2->gch)
		return 0;
	if (s->as.type == ABC_T_REST) {
		if (s2->as.type  == ABC_T_REST
		 && (s->as.flags & ABC_F_INVIS) && !(s2->as.flags & ABC_F_INVIS))
			return 0;
		return 1;
	}
	if (s2->ly
	 || (s2->sflags & (S_SL1 | S_SL2))
	 || s2->as.u.note.slur_st != 0
	 || s2->as.u.note.slur_end != 0)
		return 0;
	if ((s2->sflags ^ s->sflags) & (S_BEAM_ST | S_BEAM_END))
		return 0;
	nhd2 = s2->nhd;
	if (s->nhd + nhd2 + 1 >= MAXHD)
		return 0;
	if (cfmt.combinevoices <= 1 && s->pits[0] <= s2->pits[nhd2] + 1)
		return 0;
	return 1;
}

/* -- combine 2 voices -- */
static void do_combine(struct SYMBOL *s)
{
	struct SYMBOL *s2;
	int i, nhd, nhd2, type;

again:
	nhd = s->nhd;
	s2 = s->ts_next;
	s2->extra = NULL;
	if (s->as.type != s2->as.type) {	/* if note and rest */
		if (s2->as.type == ABC_T_REST)
			goto delsym2;
		s2 = s;
		s = s2->ts_next;
		goto delsym2;
	}
	if (s->as.type == ABC_T_REST) {
		if ((s->as.flags & ABC_F_INVIS)
		 && !(s2->as.flags & ABC_F_INVIS))
			s->as.flags &= ~ABC_F_INVIS;
		goto delsym2;
	}

	/* combine the voices */
	nhd2 = s2->nhd + 1;
	memcpy(&s->pits[nhd + 1], s2->pits,
		sizeof s->pits[0] * nhd2);
#define COMBINEV(f)\
    memcpy(&s->as.u.note.f[nhd + 1], s2->as.u.note.f,\
	sizeof s->as.u.note.f[0] * nhd2);\

		COMBINEV(pits);
		COMBINEV(lens);
		COMBINEV(accs);
		COMBINEV(sl1);
		COMBINEV(sl2);
		COMBINEV(ti1);
//		COMBINEV(decs);
#undef COMBINEV
	if (s2->as.u.note.dc.n > 0) {	// update the added decorations
		int n;

		for (i = 0; i < s2->as.u.note.dc.n; i++) {
			if (s2->as.u.note.dc.tm[i].m != 255)
				s2->as.u.note.dc.tm[i].m += nhd + 1;
		}
		n = s->as.u.note.dc.n;
		memcpy(&s->as.u.note.dc.tm[n],
			s2->as.u.note.dc.tm,
			sizeof s->as.u.note.dc.tm[0] * s2->as.u.note.dc.n);
		s->as.u.note.dc.n += s2->as.u.note.dc.n;
	}

	nhd += nhd2;
	s->nhd = nhd;

	sort_pitch(s, 1);		/* sort the notes by pitch */
	s->ymx = 3 * (s->pits[nhd] - 18) + 4;
	s->ymn = 3 * (s->pits[0] - 18) - 4;
	s->yav = (s->ymx + s->ymn) / 2;

	/* force the tie directions */
	type = s->as.u.note.ti1[0];
	if ((type & 0x03) == SL_AUTO)
		s->as.u.note.ti1[0] = SL_BELOW | (type & ~SL_DOTTED);
	type = s->as.u.note.ti1[nhd];
	if ((type & 0x03) == SL_AUTO)
		s->as.u.note.ti1[nhd] = SL_ABOVE | (type & ~SL_DOTTED);
delsym2:
	if (s2->as.text && !s->as.text) {
		s->as.text = s2->as.text;
		s->gch = s2->gch;
	}
	unlksym(s2);			/* remove the next symbol */

	/* there may be more voices */
	if (!(s->sflags & S_IN_TUPLET) && may_combine(s))
		goto again;
}

/* -- try to combine voices */
static void combine_voices(void)
{
	struct SYMBOL *s, *s2, *g;
	int i, r;

	for (s = tsfirst; s->ts_next; s = s->ts_next) {
		if (cfmt.combinevoices == 0
		 && s->as.type != ABC_T_REST)
			continue;
		if (s->sflags & S_IN_TUPLET) {
			g = s->extra;
			if (!g)
				continue;	/* tuplet already treated */
			r = 0;
			for ( ; g; g = g->next) {
				if (g->type == TUPLET
				 && g->as.u.tuplet.r_plet > r)
					r = g->as.u.tuplet.r_plet;
			}
			if (r == 0)
				continue;
			i = r;
			for (s2 = s; s2; s2 = s2->next) {
				if (!s2->ts_next)
					break;
				if (s2->type != NOTEREST)
					continue;
				if (!may_combine(s2))
					break;
				if (--i <= 0)
					break;
			}
			if (i > 0)
				continue;
			for (s2 = s; /*s2*/; s2 = s2->next) {
				if (s2->type != NOTEREST)
					continue;
				do_combine(s2);
				if (--r <= 0)
					break;
			}
			continue;
			
		}
		if (s->type != NOTEREST)
			continue;

		if (s->as.type == ABC_T_NOTE) {
			if (!(s->sflags & S_BEAM_ST))
				continue;
			if (s->sflags & S_BEAM_END) {
				if (may_combine(s))
					do_combine(s);
				continue;
			}
		} else {
			if (may_combine(s))
				do_combine(s);
			continue;
		}
		s2 = s;
		for (;;) {
			if (!may_combine(s2)) {
				s2 = NULL;
				break;
			}
//fixme: may have rests in beam
			if (s2->sflags & S_BEAM_END)
				break;
			do {
				s2 = s2->next;
			} while (s2->type != NOTEREST);
		}
		if (!s2)
			continue;
		s2 = s;
		for (;;) {
			do_combine(s2);
//fixme: may have rests in beam
			if (s2->sflags & S_BEAM_END)
				break;
			do {
				s2 = s2->next;
			} while (s2->type != NOTEREST);
		}
	}
}

/* -- insert a clef change (treble or bass) before a symbol -- */
static struct SYMBOL *insert_clef(struct SYMBOL *s,
				int clef_type,
				int clef_line)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *new_s;
	int staff;

	staff = s->staff;

	/* don't insert the clef between two bars */
	if (s->type == BAR && s->prev && s->prev->type == BAR
/*	 && s->time == s->prev->time */
			)
		s = s->prev;

	/* create the symbol */
	p_voice = &voice_tb[s->voice];
	p_voice->last_sym = s->prev;
	if (!p_voice->last_sym)
		p_voice->sym = NULL;
	p_voice->time = s->time;
	new_s = sym_add(p_voice, CLEF);
	new_s->next = s;
	s->prev = new_s;

	new_s->as.u.clef.type = clef_type;
	new_s->as.u.clef.line = clef_line;
	new_s->staff = staff;
	new_s->u = 1;			/* small clef */
	new_s->sflags &= ~S_SECOND;

	/* link in time */
	while (!(s->sflags & S_SEQST))
		s = s->ts_prev;
//	if (!s->ts_prev || s->ts_prev->type != CLEF)
	if (s->ts_prev->type != CLEF)
		new_s->sflags |= S_SEQST;
	new_s->ts_prev = s->ts_prev;
//	if (new_s->ts_prev)
		new_s->ts_prev->ts_next = new_s;
//	else
//		tsfirst = new_s;
	new_s->ts_next = s;
	s->ts_prev = new_s;
	return new_s;
}

/* -- set the staff of the floating voices -- */
/* this function is called only once per tune */
static void set_float(void)
{
	struct VOICE_S *p_voice;
	int staff, staff_chg;
	struct SYMBOL *s, *s1;

	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (!p_voice->floating)
			continue;
		staff_chg = 0;
		staff = p_voice->staff;
		for (s = p_voice->sym; s; s = s->next) {
			signed char up, down;

			if (s->as.type != ABC_T_NOTE) {
				if (staff_chg)
					s->staff++;
				continue;
			}
			if (!(s->sflags & S_FLOATING)) {
				staff_chg = 0;
				continue;
			}
			if (s->pits[0] >= 19) {		/* F */
				staff_chg = 0;
				continue;
			}
			if (s->pits[s->nhd] <= 12) {	/* F, */
				staff_chg = 1;
				s->staff++;
				continue;
			}
			up = 127;
			for (s1 = s->ts_prev; s1; s1 = s1->ts_prev) {
				if (s1->staff != staff
				 || s1->voice == s->voice)
					break;
#if 1
/*fixme:test again*/
				if (s1->as.type == ABC_T_NOTE)
#endif
				    if (s1->pits[0] < up)
					up = s1->pits[0];
			}
			if (up == 127) {
				if (staff_chg)
					s->staff++;
				continue;
			}
			if (s->pits[s->nhd] > up - 3) {
				staff_chg = 0;
				continue;
			}
			down = -127;
			for (s1 = s->ts_next; s1; s1 = s1->ts_next) {
				if (s1->staff != staff + 1
				 || s1->voice == s->voice)
					break;
#if 1
/*fixme:test again*/
				if (s1->as.type == ABC_T_NOTE)
#endif
				    if (s1->pits[s1->nhd] > down)
					down = s1->pits[s1->nhd];
			}
			if (down == -127) {
				if (staff_chg)
					s->staff++;
				continue;
			}
			if (s->pits[0] < down + 3) {
				staff_chg = 1;
				s->staff++;
				continue;
			}
			up -= s->pits[s->nhd];
			down = s->pits[0] - down;
			if (!staff_chg) {
				if (up < down + 3)
					continue;
				staff_chg = 1;
			} else {
				if (up < down - 3) {
					staff_chg = 0;
					continue;
				}
			}
			s->staff++;
		}
	}
}

/* -- set the x offset of the grace notes -- */
static float set_graceoffs(struct SYMBOL *s)
{
	struct SYMBOL *g, *next;
	int m;
	float xx, gspleft, gspinside, gspright;

	gspleft = (cfmt.gracespace >> 16) * 0.1;
	gspinside = ((cfmt.gracespace >> 8) & 0xff) * 0.1;
	gspright = (cfmt.gracespace & 0xff) * 0.1;
	xx = 0;
	for (g = s->extra; ; g = g->next) {
		if (g->type == NOTEREST)
			break;
	}
	g->sflags |= S_BEAM_ST;
	for ( ; ; g = g->next) {
		if (g->type != NOTEREST) {
			if (!g->next)
				break;
			continue;
		}
		set_head_directions(g);
		for (m = g->nhd; m >= 0; m--) {
			if (g->as.u.note.accs[m]) {
				xx += 5;
				if (g->as.u.note.accs[m] & 0xf8)
					xx += 2;
				break;
			}
		}
		g->x = xx;

		if (g->nflags <= 0)
			g->sflags |= S_BEAM_ST | S_BEAM_END;
		next = g->next;
		if (!next) {
			g->sflags |= S_BEAM_END;
			break;
		}
		if (next->nflags <= 0 || (next->as.flags & ABC_F_SPACE))
			g->sflags |= S_BEAM_END;
		if (g->sflags & S_BEAM_END) {
			next->sflags |= S_BEAM_ST;
			xx += gspinside / 4;
		}
		if (g->nflags <= 0)
			xx += gspinside / 4;
		if (g->y > next->y + 8)
			xx -= 1.5;
		xx += gspinside;
	}

	xx += gspleft + gspright;
	next = s->next;
	if (next
	 && next->as.type == ABC_T_NOTE) {	/* if before a note */
		if (g->y >= 3 * (next->pits[next->nhd] - 18))
			xx -= 1;		/* above, a bit closer */
		else if ((g->sflags & S_BEAM_ST)
		      && g->y < 3 * (next->pits[0] - 18) - 7)
			xx += 2;	/* below with flag, a bit further */
	}

	/* return the whole width */
	return xx;
}

/* -- compute the width needed by the guitar chords / annotations -- */
static float gchord_width(struct SYMBOL *s,
			  float wlnote,
			  float wlw)
{
	struct SYMBOL *s2;
	struct gch *gch;
	int ix;
	float lspc, rspc, w;

	lspc = rspc = 0;
	for (ix = 0, gch = s->gch; ix < MAXGCH; ix++, gch++) {
		if (gch->type == '\0')
			break;
		switch (gch->type) {
		default: {		/* default = above */
			float wl;

			wl = -gch->x;
			if (wl > lspc)
				lspc = wl;
			w = gch->w + 2 - wl;
			if (w > rspc)
				rspc = w;
			break;
		    }
		case '<':		/* left */
			w = gch->w + wlnote;
			if (w > lspc)
				lspc = w;
			break;
		case '>':		/* right */
			w = gch->w + s->wr;
			if (w > rspc)
				rspc = w;
			break;
		}
	}

	/* adjust width for no clash */
	s2 = s->prev;
	if (s2 && s2->gch) {
		for (s2 = s->ts_prev; ; s2 = s2->ts_prev) {
			if (s2 == s->prev) {
				AT_LEAST(wlw, lspc);
				break;
			}
			if (s2->sflags & S_SEQST)
				lspc -= s2->shrink;
		}
	}
	s2 = s->next;
	if (s2 && s2->gch) {
		for (s2 = s->ts_next; ; s2 = s2->ts_next) {
			if (s2 == s->next) {
				AT_LEAST(s->wr, rspc);
				break;
			}
			if (s2->sflags & S_SEQST)
				rspc -= 8;
		}
	}
	return wlw;
}

/* -- set the width needed by the lyrics -- */
static float ly_width(struct SYMBOL *s, float wlw)
{
	struct SYMBOL *k;
	struct lyrics *ly = s->ly;
	struct lyl *lyl;
	struct tblt_s *tblt;
	float align, xx, w;
	int i;

	/* check if the lyrics contain tablature definition */
	for (i = 0; i < 2; i++) {
		tblt = voice_tb[s->voice].tblts[i];
		if (!tblt)
			continue;
		if (tblt->pitch == 0) {		/* yes, no width */
			for (i = 0; i < MAXLY; i++) {
				if ((lyl = ly->lyl[i]) == 0)
					continue;
				lyl->s = 0;
			}
			return wlw;
		}
	}

	align = 0;
	for (i = 0; i < MAXLY; i++) {
		float swfac, shift;
		char *p;

		lyl = ly->lyl[i];
		if (!lyl)
			continue;
		p = lyl->t;
		w = lyl->w;
		swfac = lyl->f->swfac;
		xx = w + 2 * cwid(' ') * swfac;
		if (isdigit((unsigned char) *p)
		 || p[1] == ':'
//		 || p[1] == '(' || p[1] == ')') {
		 || *p == '(' || *p == ')') {
			float sz;

//			if (p[1] == '(')
			if (*p == '(') {
//				sz = cwid((unsigned char) p[1]);
				sz = cwid((unsigned char) *p);
			} else {
				sz = 0;
				while (*p != '\0') {
/*fixme: KO when '\ooo'*/
					if (*p == '\\') {
						p++;
						continue;
					}
					sz += cwid((unsigned char) *p);
					if (*p == ' ')
						break;
					p++;
				}
			}
			sz *= swfac;
			shift = (w - sz + 2 * cwid(' ') * swfac)
				* VOCPRE;
			if (shift > 20)
				shift = 20;
			shift += sz;
			if (isdigit((unsigned char) lyl->t[0])) {
				if (shift > align)
					align = shift;
			}
		} else if (*p == LY_HYPH || *p == LY_UNDER) {
			shift = 0;
		} else {
			shift = xx * VOCPRE;
			if (shift > 20)
				shift = 20;
		}
		lyl->s = shift;
		AT_LEAST(wlw, shift);
		xx -= shift;
		shift = 2 * cwid(' ') * swfac;
		for (k = s->next; k; k = k->next) {
			switch (k->type) {
			case NOTEREST:
				if (!k->ly
				 || !k->ly->lyl[i])
					xx -= 9;
				else if (k->ly->lyl[i]->t[0] == LY_HYPH
				      || k->ly->lyl[i]->t[0] == LY_UNDER)
					xx -= shift;
				else
					break;
				if (xx <= 0)
					break;
				continue;
			case CLEF:
			case TIMESIG:
			case KEYSIG:
				xx -= 10;
				continue;
			default:
				xx -= 5;
				break;
			}
			break;
		}
		if (xx > s->wr)
			s->wr = xx;
		}
	if (align > 0) {
		for (i = 0; i < MAXLY; i++) {
			if ((lyl = ly->lyl[i]) == 0)
				continue;
			if (isdigit((unsigned char) lyl->t[0]))
				lyl->s = align;
		}
	}
	return wlw;
}

/* -- set the width of a symbol -- */
/* This routine sets the minimal left and right widths wl,wr
 * so that successive symbols are still separated when
 * no extra glue is put between them */
static void set_width(struct SYMBOL *s)
{
	struct SYMBOL *s2;
	int i, m;
	float xx, w, wlnote, wlw;

	switch (s->type) {
	case NOTEREST:

		/* set the note widths */
		switch (s->head) {
		case H_SQUARE:
			wlnote = 8;
			break;
		case H_OVAL:
			wlnote = 6;
			break;
		case H_EMPTY:
			wlnote = 5;
			break;
		default:
			wlnote = 4.5;
			break;
		}
		s->wr = wlnote;

		/* room for shifted heads and accidental signs */
		if (s->xmx > 0)
			s->wr += s->xmx + 4;
		s2 = s->prev;
		if (s2) {
			switch (s2->type) {
			case BAR:
			case CLEF:
			case KEYSIG:
			case TIMESIG:
				wlnote += 3;
				break;
			}
		}
		for (m = 0; m <= s->nhd; m++) {
			xx = s->shhd[m];
			if (xx < 0)
				AT_LEAST(wlnote, -xx + 5);
			if (s->as.u.note.accs[m]) {
				AT_LEAST(wlnote, s->shac[m]
					 + ((s->as.u.note.accs[m] & 0xf8)
					    ? 6.5 : 4.5));
			}
		}
		if (s2) {
			switch (s2->type) {
			case BAR:
			case CLEF:
			case KEYSIG:
			case TIMESIG:
				wlnote -= 3;
				break;
			}
		}

		/* room for the decorations */
		if (s->as.u.note.dc.n != 0)
			wlnote += deco_width(s);

		/* space for flag if stem goes up on standalone note */
		if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == (S_BEAM_ST | S_BEAM_END)
		 && s->stem > 0 && s->nflags > 0)
			AT_LEAST(s->wr, s->xmx + 9);	// was 12, then removed, then back again

		/* leave room for dots and set their offset */
		if (s->dots > 0) {
			switch (s->head) {
			case H_SQUARE:
			case H_OVAL:
				s->xmx += 2;
				break;
			case H_EMPTY:
				s->xmx += 1;
				break;
			}
			AT_LEAST(s->wr, s->xmx + 12);
			if (s->dots >= 2)
				s->wr += 3.5 * (s->dots - 1);
		}

		/* if a tremolo on 2 notes, have space for the small beam(s) */
		if ((s->sflags & (S_TREM2 | S_BEAM_END)) == (S_TREM2 | S_BEAM_END))
			AT_LEAST(wlnote, 20);

		wlw = wlnote;

		if (s2) {
			switch (s2->type) {
			case NOTEREST:	/* extra space when up stem - down stem */
				if (s2->as.type == ABC_T_REST)
					break;
				if (s2->stem > 0 && s->stem < 0)
					AT_LEAST(wlw, 7);

				/* make sure helper lines don't overlap */
				if ((s->y > 27 && s2->y > 27)
				 || (s->y < -3 && s2->y < -3))
					AT_LEAST(wlw, 6);

				/* have ties wide enough */
				if (s2->sflags & S_TI1)
					AT_LEAST(wlw, 14);
				break;
			case CLEF:		/* extra space at start of line */
				if ((s2->sflags & S_SECOND)
				 || s2->u)
					break;
				wlw += 8;
				break;
			case KEYSIG:
/*			case TIMESIG:	*/
				wlw += 4;
				break;
			}
		}

		/* leave room for guitar chord */
		if (s->gch)
			wlw = gchord_width(s, wlnote, wlw);

		/* leave room for vocals under note */
		/* related to draw_lyrics() */
		if (s->ly)
			wlw = ly_width(s, wlw);

		/* if preceeded by a grace note sequence, adjust */
		if (s2 && s2->type == GRACE)
			s->wl = wlnote - 4.5;
		else
			s->wl = wlw;
		break;
	case SPACE:
		if (s->as.u.note.lens[1] < 0)
			xx = 10;
		else
			xx = s->as.u.note.lens[1] * 0.5;
		s->wr = xx;
		if (s->gch)
			xx = gchord_width(s, xx, xx);
		if (s->as.u.note.dc.n != 0)
			xx += deco_width(s);
		s->wl = xx;
		break;
	case BAR:
		if (s->sflags & S_NOREPBRA)
			break;
		if (!(s->as.flags & ABC_F_INVIS)) {
			int bar_type;

			w = 5;
			bar_type = s->as.u.bar.type;
			switch (bar_type) {
			case (B_BAR << 4) + B_COL:
			case (B_COL << 4) + B_BAR:
				w += 3 + 3 + 5;
				break;
			case (B_COL << 4) + B_COL:
				w += 5 + 3 + 3 + 3 + 5;
				break;
			default:
				for (;;) {
					switch (bar_type & 0x0f) {
					case B_OBRA:
					case B_CBRA:
						w += 3;
						break;
					case B_COL:
						w += 2;
						break;
					}
					bar_type >>= 4;
					if (bar_type == 0)
						break;
					w += 3;
				}
				break;
			}
			s->wl = w;
			if (s->next
			 && s->next->type != TIMESIG)
				s->wr = 8;
			else
				s->wr = 5;
//			s->shhd[0] = (w - 5) * -0.5;
		}
		if (s->as.u.bar.dc.n != 0)
			s->wl += deco_width(s);

		/* have room for the repeat numbers / guitar chord */
		if (s->gch
		 && strlen(s->as.text) < 4)
			s->wl = gchord_width(s, s->wl, s->wl);
		break;
	case CLEF:
		/* shift the clef to the left - see draw_symbols() */
		if (!(s->as.flags & ABC_F_INVIS)) {
			s->wl = 12 + 10;
			s->wr = (s->u ? 10 : 12) - 10;
		}
		break;
	case KEYSIG: {
		int n1, n2, esp;

		s->wl = 3;
		esp = 4;
		if (s->as.u.key.nacc == 0) {
			n1 = s->as.u.key.sf;	/* new key sig */
			if (cfmt.cancelkey || n1 == 0)
				n2 = s->u;	/* old key */
			else
				n2 = 0;
			if (n1 * n2 >= 0) {	/* if no natural */
				if (n1 < 0)
					n1 = -n1;
				if (n2 < 0)
					n2 = -n2;
				if (n2 > n1)
					n1 = n2;
			} else {
				n1 -= n2;
				if (n1 < 0)
					n1 = -n1;
				esp += 3;	/* see extra space in draw_keysig() */
			}
		} else {
			int last_acc;

			n1 = n2 = s->as.u.key.nacc;
			last_acc = s->as.u.key.accs[0];
			for (i = 1; i < n2; i++) {
				if (s->as.u.key.pits[i] > s->as.u.key.pits[i - 1] + 6
				 || s->as.u.key.pits[i] < s->as.u.key.pits[i - 1] - 6)
					n1--;		/* octave */
				else if (s->as.u.key.accs[i] != last_acc)
					esp += 3;
				last_acc = s->as.u.key.accs[i];
			}
		}
		s->wr = 5.5 * n1 + esp;
		break;
	    }
	case TIMESIG:
		/* !!tied to draw_timesig()!! */
		w = 0;
		for (i = 0; i < s->as.u.meter.nmeter; i++) {
			int l;

			l = sizeof s->as.u.meter.meter[i].top;
			if (s->as.u.meter.meter[i].top[l - 1] == '\0') {
				l = strlen(s->as.u.meter.meter[i].top);
				if (s->as.u.meter.meter[i].top[1] == '|'
				 || s->as.u.meter.meter[i].top[1] == '.')
					l--;		/* 'C|' */
			}
			if (s->as.u.meter.meter[i].bot[0] != '\0') {
				int l2;

				l2 = sizeof s->as.u.meter.meter[i].bot;
				if (s->as.u.meter.meter[i].bot[l2 - 1] == '\0')
					l2 = strlen(s->as.u.meter.meter[i].bot);
				if (l2 > l)
					l = l2;
			}
			w += 6.5 * l;
		}
		s->wl = w;
		s->wr = w + 7;
		break;
	case MREST:
		s->wl = 40 / 2 + 16;
		s->wr = 40 / 2 + 16;
		break;
	case GRACE:
		s->wl = set_graceoffs(s);
		break;
	case STBRK:
		if (s->next && s->next->type == CLEF) {
			s->wr = 2;
			s->next->u = 0;	/* big clef */
		} else {
			s->wr = 8;
		}
		s->wl = s->xmx;
		break;
#if 0
	case TEMPO:
	case PART:
	case TUPLET:
	case CUSTOS:
#endif
	case FMTCHG:		/* no space */
		break;
	default:
		bug("Cannot set width for symbol", 1);
	}
}

/* -- set the natural space -- */
static float set_space(struct SYMBOL *s)
{
	struct SYMBOL *s2;
	int i, len, l, stemdir, prev_time;
	float space;

//fixme: s->ts_prev never NULL ?
//	prev_time = !s->ts_prev ? s->time : s->ts_prev->time;
	prev_time = s->ts_prev->time;
	len = s->time - prev_time;		/* time skip */
	if (len == 0) {
		switch (s->type) {
		case MREST:
			return s->wl + 16;
/*fixme:do same thing at start of line*/
		case NOTEREST:
			if (s->ts_prev->type == BAR) {
				i = 2;
				if (s->nflags < -2)
					i = 0;
				return space_tb[i];
			}
			break;
		}
		return 0;
	}
	if (s->ts_prev->type == MREST)
		return s->ts_prev->wr + 16
				+ 3;		// (bar wl=5 wr=8)
	if (smallest_duration >= MINIM) {
		if (smallest_duration >= SEMIBREVE)
			len /= 4;
		else
			len /= 2;
	}
	if (len >= CROTCHET) {
		if (len < MINIM)
			i = 5;
		else if (len < SEMIBREVE)
			i = 6;
		else if (len < BREVE)
			i = 7;
		else if (len < BREVE * 2)
			i = 8;
		else
			i = 9;
	} else {
		if (len >= QUAVER)
			i = 4;
		else if (len >= SEMIQUAVER)
			i = 3;
		else if (len >= SEMIQUAVER / 2)
			i = 2;
		else if (len >= SEMIQUAVER / 4)
			i = 1;
		else
			i = 0;
	}
	l = len - ((SEMIQUAVER / 8) << i);
	space = space_tb[i];
	if (l != 0) {
		if (l < 0) {
			space = space_tb[0] * len / (SEMIQUAVER / 8);
		} else {
			if (i >= 9)
				i = 8;
			space += (space_tb[i + 1] - space_tb[i])
				* l / len;
		}
	}
	if (s->dur == 0) {
		if (s->type == BAR) {
			if (s->as.u.bar.type & 0xf0)
				space *= 0.8;	/* complex bar */
			else
				space *= 0.7;
		}
		return space;
	}

	/* reduce spacing within a beam */
	if (!(s->sflags & S_BEAM_ST))
		space *= fnnp;

	/* decrease spacing when stem down followed by stem up */
/*fixme:to be done later, after x computed in sym_glue*/
	if (s->as.type == ABC_T_NOTE && s->nflags >= -1
	 && s->stem > 0) {
		stemdir = 1;
		for (s2 = s->ts_prev;
		     s2 && s2->time == prev_time;
		     s2 = s2->ts_prev) {
			if (s2->nflags < -1 || s2->stem > 0) {
				stemdir = 0;
				break;
			}
		}
		if (stemdir) {
			for (s2 = s->ts_next;
			     s2 && s2->time == s->time;
			     s2 = s2->ts_next) {
				if (s2->nflags < -1 || s2->stem < 0) {
					stemdir = 0;
					break;
				}
			}
			if (stemdir)
				space *= 0.9;
		}
	}
	return space;
}

/* -- set the width and space of all symbols -- */
/* this function is called once for the whole tune
 * then, once per music line up to the first sequence */
static void set_allsymwidth(struct SYMBOL *last_s)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s, *s2, *s3;
	struct tblt_s *tblt;
	int i;
	float new_val, shrink, space;

	/* set the space of the starting symbols */
	new_val = 0;
	s = tsfirst;
	for (;;) {
		set_width(s);
		if (new_val < s->wl)
			new_val = s->wl;
		s = s->ts_next;
		if (s == last_s || (s->sflags & S_SEQST))
			break;
	}
	tsfirst->shrink = new_val;

	/* loop on all remaining symbols */
	while (s != last_s) {
		s2 = s;
		shrink = space = 0;
		do {
			int ymx1, ymn1, ymx2, ymn2;
			float wl;

			/* set the minimum space before and after the symbol */
			set_width(s2);

			/* calculate the minimum space before the symbol,
			 * looping in the previous time sequence */
			if (s2->type == BAR) {
				ymx1 = 50;
				ymn1 = -50;
			} else {
				ymx1 = s2->ymx;
				ymn1 = s2->ymn;
			}
			wl = s2->wl;
			new_val = 0;
			for (s3 = s->ts_prev; s3; s3 = s3->ts_prev) {
				if (new_val < s3->wr
				 && s3->type == NOTEREST
				 && s2->type == NOTEREST)
					new_val = s3->wr;
				if (s3->staff == s2->staff
				 && (!(s3->as.flags & ABC_F_INVIS)
				  || s3->voice == s2->voice)
				 && new_val < s3->wr + wl) {
					switch (s3->type) {
					case NOTEREST:
						if (s2->type == NOTEREST) {
							new_val = s3->wr + wl;
							break;
						}
						/* fall thru */
					default:
						ymx2 = s3->ymx;
						ymn2 = s3->ymn;
						if (ymn1 > ymx2
						 || ymx1 < ymn2)
							break;
						/* fall thru */
					case SPACE:
					case BAR:
					case CLEF:
					case TIMESIG:
					case KEYSIG:
						new_val = s3->wr + wl;
						break;
					}
				}
				if (s3->sflags & S_SEQST) {
					if (new_val != 0)
						break;
					wl -= s3->shrink;
					if (wl < 0)
						break;
				}
			}
			if (shrink < new_val)
				shrink = new_val;
			new_val = set_space(s2);
			if (space < new_val)
				space = new_val;
			if ((s2 = s2->ts_next) == last_s)
				break;
		} while (!(s2->sflags & S_SEQST));

		/* set the spaces at start of sequence */
		if (shrink == 0 && space == 0 && s->type == CLEF) {
			s->sflags &= ~S_SEQST;		/* no space */
			s->time = s->ts_prev->time;
		} else {
			s->shrink = shrink;
			s->space = space;
		}
		s = s2;
	}

	/* have room for the tablature header */
	space = 0;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		for (i = 0; i < 2; i++) {
			if ((tblt = p_voice->tblts[i]) == NULL)
				continue;
			if (tblt->wh > space)
				space = tblt->wh;
		}
	}
	if (space == 0)
		return;
	shrink = 0;
	for (s = tsfirst; s != last_s; s = s->ts_next) {
		if (s->shrink != 0)
			shrink += s->shrink;
		if (s->as.type == ABC_T_NOTE)
			break;
	}
	if (s != last_s && shrink < space) {
		while (!(s->sflags & S_SEQST))
			s = s->ts_prev;
		s->shrink += space - shrink;
	}
}

/* change a symbol into a rest */
static void to_rest(struct SYMBOL *s)
{
	s->type = NOTEREST;
	s->as.type = ABC_T_REST;
	s->sflags &= S_NL | S_SEQST;
	s->doty = -1;
	s->as.u.note.dc.n = 0;
	s->gch = NULL;
	s->extra = NULL;
	s->as.u.note.slur_st = s->as.u.note.slur_end = 0;
/*fixme: should set many parameters for set_width*/
//	set_width(s);
}

/* -- set the repeat sequences / measures -- */
static void set_repeat(struct SYMBOL *g,	/* repeat format */
			struct SYMBOL *s)	/* first note */
{
	struct SYMBOL *s2, *s3;
	int i, j, n, dur, staff, voice;

	staff = s->staff;
	voice = s->voice;

	/* treat the sequence repeat */
	if ((n = g->doty) < 0) {		/* number of notes / measures */
		n = -n;
		i = n;				/* number of notes to repeat */
		for (s3 = s->prev; s3; s3 = s3->prev) {
			if (s3->dur == 0) {
				if (s3->type == BAR) {
					error(0, s3, "Bar in sequence to repeat");
					goto delrep;
				}
				continue;
			}
			if (--i <= 0)
				break;
		}
		if (!s3) {
			error(0, s, "Not enough symbols to repeat");
			goto delrep;
		}

		i = g->nohdi1 * n;	/* number of notes/rests to repeat */
		for (s2 = s; s2; s2 = s2->next) {
			if (s2->dur == 0) {
				if (s2->type == BAR) {
					error(0, s2, "Bar in repeat sequence");
					goto delrep;
				}
				continue;
			}
			if (--i <= 0)
				break;
		}
		if (!s2
		 || !s2->next) {		/* should have some symbol */
			error(0, s, "Not enough symbols after repeat sequence");
			goto delrep;
		}
		for (s2 = s->prev; s2 != s3; s2 = s2->prev) {
			if (s2->as.type == ABC_T_NOTE) {
				s2->sflags |= S_BEAM_END;
				break;
			}
		}
		s3 = s;
		for (j = g->nohdi1; --j >= 0; ) {
			i = n;			/* number of notes/rests */
			if (s3->dur != 0)
				i--;
			s2 = s3->ts_next;
			while (i > 0) {
				if (s2->staff != staff)
					continue;
				if (s2->voice == voice
				 && s2->dur != 0)
					i--;
				s2->extra = NULL;
				unlksym(s2);
				s2 = s2->ts_next;
			}
			to_rest(s3);
			s3->dur = s3->as.u.note.lens[0]
				= s2->time - s3->time;
//			s3->sflags |= S_REPEAT | S_BEAM_ST;
			s3->sflags |= S_REPEAT;
			set_width(s3);
			if (s3->sflags & S_SEQST)
				s3->space = set_space(s3);
			s3->head = H_SQUARE;
			s3 = s2;
		}
		goto delrep;			/* done */
	}

	/* check the measure repeat */
	i = n;				/* number of measures to repeat */
	for (s2 = s->prev->prev ; s2; s2 = s2->prev) {
		if (s2->type == BAR
		 || s2->time == tsfirst->time) {
			if (--i <= 0)
				break;
		}
	}
	if (!s2) {
		error(0, s, "Not enough measures to repeat");
		goto delrep;
	}

	dur = s->time - s2->time;	/* repeat duration */

	if (n == 1)
		i = g->nohdi1;		/* repeat number */
	else
		i = n;			/* check only 2 measures */
	for (s2 = s; s2; s2 = s2->next) {
		if (s2->type == BAR) {
			if (--i <= 0)
				break;
		}
	}
	if (!s2) {
		error(0, s, "Not enough bars after repeat measure");
		goto delrep;
	}

	/* if many 'repeat 2 measures'
	 * insert a new %%repeat after the next bar */
	i = g->nohdi1;		/* repeat number */
	if (n == 2 && i > 1) {
		s2 = s2->next;
		if (!s2) {
			error(0, s, "Not enough bars after repeat measure");
			goto delrep;
		}
		g->nohdi1 = 1;
		s = (struct SYMBOL *) getarena(sizeof *s);
		memcpy(s, g, sizeof *s);
		s->next = s2->extra;
		if (s->next)
			s->next->prev = s;
		s->prev = NULL;
		s2->extra = s;
		s->nohdi1 = --i;
	}

	/* replace */
	dur /= n;
	if (n == 2) {			/* repeat 2 measures (once) */
		s3 = s;
		for (s2 = s->ts_next; ;s2 = s2->ts_next) {
			if (s2->staff != staff)
				continue;
			if (s2->voice == voice
			 && s2->type == BAR)
				break;
			s2->extra = NULL;
			unlksym(s2);
		}
		to_rest(s3);
		s3->dur = s3->as.u.note.lens[0] = dur;
		s3->as.flags = ABC_F_INVIS;
		if (s3->sflags & S_SEQST)
			s3->space = set_space(s3);
		s2->as.u.bar.len = 2;
		if (s2->sflags & S_SEQST)
			s2->space = set_space(s2);
		s3 = s2->next;
		s2 = s3->next;
		for (;;) {
			if (s2->type == BAR || s2->type == CLEF)
				break;
			s2->extra = NULL;
			unlksym(s2);
			s2 = s2->next;
		}
		to_rest(s3);
		s3->dur = s3->as.u.note.lens[0] = dur;
		s3->as.flags = ABC_F_INVIS;
		set_width(s3);
		if (s3->sflags & S_SEQST)
			s3->space = set_space(s3);
		if (s2->sflags & S_SEQST)
			s2->space = set_space(s2);
		return;
	}

	/* repeat 1 measure */
	s3 = s;
	for (j = g->nohdi1; --j >= 0; ) {
		for (s2 = s3->ts_next; ; s2 = s2->ts_next) {
			if (s2->staff != staff)
				continue;
			if (s2->voice == voice
			 && s2->type == BAR)
				break;
			s2->extra = NULL;
			unlksym(s2);
		}
		to_rest(s3);
		s3->dur = s3->as.u.note.lens[0] = dur;
//		s3->sflags |= S_REPEAT | S_BEAM_ST;
		s3->sflags |= S_REPEAT;
		if (s3->sflags & S_SEQST)
			s3->space = set_space(s3);
		if (s2->sflags & S_SEQST)
			s2->space = set_space(s2);
		if (g->nohdi1 == 1) {
			s3->doty = 1;
			break;
		}
		s3->doty = g->nohdi1 - j + 1;	/* number to print above the repeat rest */
		s3 = s2->next;
	}
	return;

delrep:					/* remove the %%repeat */
	g->u = -1;
}

/* add a custos before the symbol of the next line */
static void custos_add(struct SYMBOL *s)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *new_s, *s2;
	int i;

	s2 = s;
	for (;;) {
		if (!s2)
			return;
		if (s2->as.type == ABC_T_NOTE)
			break;
		s2 = s2->next;
	}

	p_voice = &voice_tb[s->voice];
	p_voice->last_sym = s->prev;
	if (!p_voice->last_sym)
		p_voice->sym = NULL;
	p_voice->time = s->time;
	new_s = sym_add(p_voice, CUSTOS);
	new_s->next = s;
	s->prev = new_s;
	new_s->ts_prev = s->ts_prev;
	new_s->ts_prev->ts_next = new_s;
	new_s->ts_next = s;
	s->ts_prev = new_s;

	new_s->sflags |= S_SEQST;
	new_s->wl = 8;
	new_s->wr = 4;
	new_s->shrink = s->shrink;
	if (new_s->shrink < 8 + 4)
		new_s->shrink = 8 + 4;
	new_s->space = s2->space;

	new_s->nhd = s2->nhd;
//	memcpy(new_s->as.u.note.lens, s2->as.u.note.lens,
//			sizeof new_s->as.u.note.lens);
	memcpy(new_s->pits, s2->pits, sizeof new_s->pits);
	for (i = 0; i <= new_s->nhd; i++)
		new_s->as.u.note.lens[i] = CROTCHET;
	new_s->as.flags = ABC_F_STEMLESS;
}

/* -- define the beginning of a new music line -- */
static struct SYMBOL *set_nl(struct SYMBOL *s)
{
	struct SYMBOL *s2, *extra;
	struct VOICE_S *p_voice;
	int done, new_sy;

	/* if explicit EOLN, cut on the next symbol */
	if ((s->sflags & S_EOLN) && !cfmt.keywarn && !cfmt.timewarn) {
		s = s->next;
		if (!s)
			return s;
		while (!(s->sflags & S_SEQST))
			s = s->ts_prev;
		goto setnl;
	}

	/* if normal symbol, cut here */
	switch (s->type) {
	case CLEF:
	case BAR:
		break;
	case KEYSIG:
		if (cfmt.keywarn && !s->as.u.key.empty)
			break;
		goto normal;
	case TIMESIG:
		if (cfmt.timewarn)
			break;
		goto normal;
	case GRACE:			/* don't cut on a grace note */
		s = s->next;
		if (!s)
			return s;
		/* fall thru */
	default:
normal:
		/* cut on the next symbol */
		s = s->next;
		if (!s)
			return s;
		while (!(s->sflags & S_SEQST))
			s = s->ts_prev;
		goto setnl;
	}

	/* go back to handle the staff breaks at end of line */
	for (; s; s = s->ts_prev) {
		if (!(s->sflags & S_SEQST))
			continue;
		switch (s->type) {
		case CLEF:
		case KEYSIG:
		case TIMESIG:
			continue;
		}
		break;
	}
	done = new_sy = 0;
	extra = NULL;
	for ( ; ; s = s->ts_next) {
		if (!s)
			return s;
		if (s->sflags & S_NEW_SY) {
			new_sy = 1;
			s->sflags &= ~S_NEW_SY;
		}
		if (!(s->sflags & S_SEQST))
			continue;
		if (done < 0)
			break;
		switch (s->type) {
		case BAR:
			if (done
			 || (s->u == 0		/* incomplete measure */
			  && s->next		/* not at end of tune */
			  && (s->as.u.bar.type & 0x0f) == B_COL
			  && !(s->sflags & S_RRBAR)))
						/* 'xx:' (not ':xx:') */
				goto cut_here;
			done = 1;
			break;
		case STBRK:
			if (s->doty == 0) {	/* if not forced */
				unlksym(s);	/* remove */
				break;
			}
			done = -1;	/* keep the next symbols on the next line */
			break;
		case TIMESIG:
			if (!cfmt.timewarn)
				goto cut_here;
			break;
		case CLEF:
			if (done)
				goto cut_here;
			break;
		case KEYSIG:
			if (!cfmt.keywarn || s->as.u.key.empty)
				goto cut_here;
			break;
		default:
			if (!done || (s->prev && s->prev->type == GRACE))
				break;
			goto cut_here;
		}
		if (s->extra) {
			if (!extra)
				extra = s;
			else
				error(0, s, "abcm2ps problem: "
					"Extra symbol may be misplaced");
		}
	}
cut_here:
	if (extra			/* extra symbol(s) to be moved */
	 && extra != s) {
		s2 = extra->extra;
		while (s2->next)
			s2 = s2->next;
		s2->next = s->extra;
		s->extra = extra->extra;
		extra->extra = NULL;
	}
	if (new_sy) {
//		for (s2 = s->ts_next; /*s*/; s2 = s2->ts_next) {
//			if (s2->sflags & S_SEQST) {
//				s2->sflags |= S_NEW_SY;
//				break;
//			}
//		}
		s->sflags |= S_NEW_SY;
	}
setnl:
	if (cfmt.custos && !first_voice->next) {
		custos_add(s);
	} else {
		s2 = s->ts_prev;
		switch (s2->type) {
		case BAR:
		case FMTCHG:
		case CLEF:
		case KEYSIG:
		case TIMESIG:
			break;
		default:			/* add an extra symbol at eol */
			p_voice = &voice_tb[s2->voice];
			p_voice->last_sym = s2;
			p_voice->time = s->time;
			s2 = s2->next;
			extra = sym_add(p_voice, FMTCHG);
			extra->next = s2;
			s2->prev = extra;
			extra->ts_prev = extra->prev;
			extra->ts_prev->ts_next = extra;
			extra->ts_next = s;
			s->ts_prev = extra;
			extra->u = -1;
			extra->sflags |= S_SEQST;
			extra->wl = 6;
			extra->wr = 6;
//			extra->shrink = extra->prev->wr + 6;
//			extra->space = extra->prev->space;
			extra->shrink = s->shrink;
			extra->space = s->space;
			if (s->x != 0) {	/* auto break */
				for (s2 = s->ts_next; ; s2 = s2->ts_next) {
					if (s2->x != 0) {
						extra->x = s2->x - 1;
						break;
					}
				}
			}
			break;
		}
	}
	s->sflags |= S_NL;
	return s;
}

/* -- search where to cut the lines according to the staff width -- */
static struct SYMBOL *set_lines(struct SYMBOL *first,	/* first symbol */
				struct SYMBOL *last,	/* last symbol / 0 */
				float lwidth,		/* w - (clef & key sig) */
				float indent)		/* for start of tune */
{
	struct SYMBOL *s, *s2, *s3;
	float x, xmin, xmax, wwidth, shrink, space;
	int nlines, beam, bar_time;

	/* calculate the whole size of the piece of tune */
	wwidth = indent;
	for (s = first; s != last; s = s->ts_next) {
		if (!(s->sflags & S_SEQST))
			continue;
		s->x = wwidth;
		shrink = s->shrink;
		if ((space = s->space) < shrink)
			wwidth += shrink;
		else
			wwidth += shrink * cfmt.maxshrink
				+ space * (1 - cfmt.maxshrink);
	}

	/* loop on cutting the tune into music lines */
	s = first;
	for (;;) {
		nlines = wwidth / lwidth + 0.999;
		if (nlines <= 1) {
			if (last)
				last = set_nl(last);
			return last;
		}

		/* try to cut on a measure bar */
		s2 = first = s;
		xmin = s->x + wwidth / nlines * cfmt.breaklimit;
		xmax = s->x + lwidth;
		for ( ; s != last; s = s->ts_next) {
			x = s->x;
			if (x == 0)
				continue;
			if (x > xmax)
				break;
			if (s->type != BAR)
				continue;
			if (x > xmin)
				goto cut_here;
			s2 = s;			// keep the last bar
		}

		bar_time = s2->time;
#if 0
		/* try to cut on a crotchet */
		if (s) {
		    for (;;) {			/* go back */
			s = s->ts_prev;
			x = s->x;
			if (x == 0)
				continue;
			if ((s->time - bar_time) % CROTCHET == 0) {
				for (s = s->ts_prev ; ; s = s->ts_prev) {
					if (s->x != 0)
						break;
				}
				goto cut_here;
			}
			if (s->x < xmin)
				break;
		    }
		}
#endif
		/* try to avoid to cut a beam */
		beam = 0;
		s = s2;				/* restart from the last bar */
		s2 = s3 = NULL;
		for ( ; s != last; s = s->ts_next) {
			if ((s->sflags & (S_BEAM_ST | S_BEAM_END))
						== S_BEAM_ST) {
				beam++;
				continue;
			}
			if ((s->sflags & (S_BEAM_ST | S_BEAM_END))
						== S_BEAM_END)
				beam--;
			x = s->x;
			if (x < xmin)
				continue;
			if (x > xmax)
				break;
			if (beam != 0)
				continue;
			s2 = s;
			if ((s->time - bar_time) % (CROTCHET / 2) == 0)
				s3 = s;
		}
		if (s3)
			s2 = s3;
		if (s2) {
			s = s2;
//			for (s = s->ts_prev ; ; s = s->ts_prev) {
//				if (s->x != 0)
//					break;
//			}
//			goto cut_here;
		}

		/* no good place, cut here */
//		for (s = s->ts_prev ; ; s = s->ts_prev) {
//			if (s->x != 0)
//				break;
//		}
		while (s->x == 0)
			s = s->ts_prev;
cut_here:
		if (s->sflags & S_NL) {		/* already set here - advance */
			error(0, s, "Line split problem - "
					"adjust maxshrink and/or breaklimit");
			nlines = 2;
			for (s = s->ts_next; s != last; s = s->ts_next) {
				if (s->x == 0)
					continue;
				if (--nlines <= 0)
					break;
			}
		}
		s = set_nl(s);
		if (!s
		 || (last && s->time >= last->time))
			break;
		wwidth -= s->x - first->x;
	}
	return s;
}

/* -- cut the tune into music lines -- */
static void cut_tune(float lwidth, float indent)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s, *s2;
	int i;
	float xmin;

	/* adjust the line width according to the starting clef
	 * and key signature */
/*fixme: may change in the tune*/
#if 1
	s = tsfirst;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		i = p_voice - voice_tb;
		if (cursys->voice[i].range >= 0)
			break;
	}
	lwidth -= 12 + 10			// clef.wl
		+ 12 - 10			// clef.wr
		+ 3				// key.wl
		+ p_voice->key.sf * 5.5;	// key.wr
#else
	for (s = tsfirst; s; s = s->ts_next) {
		if (s->shrink == 0)
			continue;
		if (s->type != CLEF && s->type != KEYSIG)
			break;
		lwidth -= s->shrink;
	}
#endif
	if (cfmt.custos && !first_voice->next)
		lwidth -= 12;
	if (cfmt.continueall) {
		set_lines(s, 0, lwidth, indent);
		return;
	}

	/* if asked, count the measures and set the EOLNs */
	if ((i = cfmt.barsperstaff) != 0) {
		s2 = s;
		for ( ; s; s = s->ts_next) {
			if (s->type != BAR
			 || s->u == 0)
				continue;
			if (--i > 0)
				continue;
			s->sflags |= S_EOLN;
			i = cfmt.barsperstaff;
		}
		s = s2;
	}

	/* cut at explicit end of line, checking the line width */
	xmin = indent;
	s2 = s;
	for ( ; s; s = s->ts_next) {
		if (!(s->sflags & (S_SEQST | S_EOLN)))
			continue;
		xmin += s->shrink;
		if (xmin > lwidth) {
			if (cfmt.linewarn)
				error(0, s, "Line overfull (%.0fpt of %.0fpt)",
					xmin, lwidth);
			for (s = s->ts_next; s; s = s->ts_next) {
				if (s->sflags & S_EOLN)
					break;
			}
			s = s2 = set_lines(s2, s, lwidth, indent);
			if (!s)
				break;
			xmin = s->shrink;
			indent = 0;
		}
		if (!(s->sflags & S_EOLN))
			continue;
		s2 = set_nl(s);
		s->sflags &= ~S_EOLN;
		s = s2;
		if (!s)
			break;
		xmin = s->shrink;
		indent = 0;
	}
}

/* -- set the y values of some symbols -- */
static void set_yval(struct SYMBOL *s)
{
//fixme: staff_tb is not yet loaded
//	int top, bot;
//	top = staff_tb[s->staff].topbar;
//	bot = staff_tb[s->staff].botbar;
	switch (s->type) {
	case CLEF:
		if ((s->sflags & S_SECOND)
		 || (s->as.flags & ABC_F_INVIS)) {
//			s->ymx = s->ymn = (top + bot) / 2;
			s->ymx = s->ymn = 12;
			break;
		}
		switch (s->as.u.clef.type) {
		default:			/* treble / perc */
			s->y = -2 * 6;
			s->ymx = 24 + 15;
			s->ymn = -11;
			break;
		case ALTO:
			s->y = -3 * 6;
			s->ymx = 24 + 6;
			s->ymn = -3;
			break;
		case BASS:
			s->y = -4 * 6;
			s->ymx = 24 + 6;
			s->ymn = -3;
			break;
		}
		if (s->u) {
			s->ymx -= 2;
			s->ymn += 2;
		}
		s->y += s->as.u.clef.line * 6;
		if (s->y > 0)
			s->ymx += s->y;
		else if (s->y < 0)
			s->ymn += s->y;
		if (s->as.u.clef.octave > 0)
			s->ymx += 9;
		else if (s->as.u.clef.octave < 0)
			s->ymn -= 9;
		break;
	case KEYSIG:
		if (s->as.u.key.sf > 2)
			s->ymx = 24 + 10;
		else if (s->as.u.key.sf > 0)
			s->ymx = 24 + 6;
		else
			s->ymx = 24 + 2;
		s->ymn = -2;
		break;
	default:
//		s->ymx = top + 2;
		s->ymx = 24 + 2;
		s->ymn = -2;
		break;
	}
}

// set the clefs (treble or bass) in a 'auto clef' sequence
// return the starting clef type
static int set_auto_clef(int staff,
			struct SYMBOL *s_start,
			int clef_type_start)
{
	struct SYMBOL *s;
	struct SYMBOL *s_last, *s_last_chg;
	int clef_type, min, max, time;

	/* get the max and min pitches in the sequence */
	max = 12;				/* "F," */
	min = 20;				/* "G" */
	for (s = s_start; s; s = s->ts_next) {
		if ((s->sflags & S_NEW_SY) && s != s_start)
			break;
		if (s->staff != staff)
			continue;
		if (s->as.type != ABC_T_NOTE) {
			if (s->type == CLEF) {
				if (s->as.u.clef.type != AUTOCLEF)
					break;
				unlksym(s);
			}
			continue;
		}
		if (s->pits[0] < min)
			min = s->pits[0];
		else if (s->pits[s->nhd] > max)
			max = s->pits[s->nhd];
	}

	if (min >= 19					/* upper than 'F' */
	 || (min >= 13 && clef_type_start != BASS))	/* or 'G,' */
		return TREBLE;
	if (max <= 13					/* lower than 'G,' */
	 || (max <= 19 && clef_type_start != TREBLE))	/* or 'F' */
		return BASS;

	/* set clef changes */
	if (clef_type_start == AUTOCLEF) {
		if ((max + min) / 2 >= 16)
			clef_type_start = TREBLE;
		else
			clef_type_start = BASS;
	}
	clef_type = clef_type_start;
	s_last = s;
	s_last_chg = NULL;
	for (s = s_start; s != s_last; s = s->ts_next) {
		struct SYMBOL *s2, *s3;	//, *s4;

		if ((s->sflags & S_NEW_SY) && s != s_start)
			break;
		if (s->staff != staff || s->as.type != ABC_T_NOTE)
			continue;

		/* check if a clef change may occur */
		time = s->time;
		if (clef_type == TREBLE) {
			if (s->pits[0] > 12		/* F, */
			 || s->pits[s->nhd] > 20) {	/* G */
				if (s->pits[0] > 20)
					s_last_chg = s;
				continue;
			}
			s2 = s->ts_prev;
			if (s2
			 && s2->time == time
			 && s2->staff == staff
			 && s2->as.type == ABC_T_NOTE
			 && s2->pits[0] >= 19)	/* F */
				continue;
			s2 = s->ts_next;
			if (s2
			 && s2->staff == staff
			 && s2->time == time
			 && s2->as.type == ABC_T_NOTE
			 && s2->pits[0] >= 19)	/* F */
				continue;
		} else {
			if (s->pits[0] < 12		/* F, */
			 || s->pits[s->nhd] < 20) {	/* G */
				if (s->pits[s->nhd] < 12)
					s_last_chg = s;
				continue;
			}
			s2 = s->ts_prev;
			if (s2
			 && s2->time == time
			 && s2->staff == staff
			 && s2->as.type == ABC_T_NOTE
			 && s2->pits[0] <= 13)	/* G, */
				continue;
			s2 = s->ts_next;
			if (s2
			 && s2->staff == staff
			 && s2->time == time
			 && s2->as.type == ABC_T_NOTE
			 && s2->pits[0] <= 13)	/* G, */
				continue;
		}

		/* if first change, change the starting clef */
		if (!s_last_chg) {
			clef_type = clef_type_start =
					clef_type == TREBLE ? BASS : TREBLE;
			s_last_chg = s;
			continue;
		}

		/* go backwards and search where to insert a clef change */
		s3 = s;
		for (s2 = s->ts_prev; s2 != s_last_chg; s2 = s2->ts_prev) {
			if (s2->staff != staff)
				continue;
			if (s2->type == BAR
			 && s2->voice == s->voice) {
				s3 = s2;
				break;
			}
			if (s2->as.type != ABC_T_NOTE)
				continue;

#if 0
			/* exit loop if a clef change cannot occur */
			if (clef_type == TREBLE) {
				if (s2->pits[0] >= 19)		/* F */
					break;
			} else {
				if (s2->pits[s2->nhd] <= 13)	/* G, */
					break;
			}
#endif

			/* have a 2nd choice on beam start */
			if ((s2->sflags & S_BEAM_ST)
			 && !voice_tb[s2->voice].second)
				s3 = s2;
		}

		/* no change possible if no insert point */
		if (s3->time == s_last_chg->time) {
			s_last_chg = s;
			continue;
		}
		s_last_chg = s;

		/* insert a clef change */
		clef_type = clef_type == TREBLE ? BASS : TREBLE;
		s2 = insert_clef(s3, clef_type, clef_type == TREBLE ? 2 : 4);
		s2->sflags |= S_CLEF_AUTO;
//		s3->prev->staff = staff;
	}
	return clef_type_start;
}

/* set the auto clefs */
/* this function is called once at start of tune generation */
/*
 * global variables:
 *	- staff_tb[staff].clef = clefs at start of line (here, start of tune)
 *				(created here, updated on clef draw)
 *	- voice_tb[voice].clef = clefs at end of generation
 *				(created on voice creation, updated here)
 */
static void set_clefs(void)
{
	struct SYSTEM *sy;
	struct VOICE_S *p_voice;
	struct SYMBOL *s, *s2, *g;
	int staff, voice, pitch, new_type, new_line, old_lvl;
	struct {
		struct SYMBOL *clef;
		int autoclef;
	} staff_clef[MAXSTAFF];

	old_lvl = lvlarena(1);			// keep the staff clefs

	// create the staff table
	memset(staff_tb, 0, sizeof staff_tb);
	for (staff = 0; staff <= nstaff; staff++) {
		staff_clef[staff].clef = NULL;
		staff_clef[staff].autoclef = 1;
	}

	// set the starting clefs of the staves
	sy = cursys;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		voice = p_voice - voice_tb;
		if (sy->voice[voice].range < 0)
			continue;
		staff = sy->voice[voice].staff;
		if (!sy->voice[voice].second) {		// main voices
			if (p_voice->stafflines >= 0)
				sy->staff[staff].stafflines = p_voice->stafflines;
			if (p_voice->staffscale != 0)
				sy->staff[staff].staffscale = p_voice->staffscale;
			if (sy->voice[voice].sep)
				sy->staff[staff].sep = sy->voice[voice].sep;
			if (sy->voice[voice].maxsep)
				sy->staff[staff].maxsep = sy->voice[voice].maxsep;
		}
		s = p_voice->s_clef;
		if (!sy->voice[voice].second
		 && !(s->sflags & S_CLEF_AUTO))
			staff_clef[staff].autoclef = 0;
	}
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		voice = p_voice - voice_tb;
		if (sy->voice[voice].range < 0
		 || sy->voice[voice].second)		// main voices
			continue;
		staff = sy->voice[voice].staff;
		s = p_voice->s_clef;
		if (staff_clef[staff].autoclef) {
			s->as.u.clef.type = set_auto_clef(staff,
							tsfirst,
							s->as.u.clef.type);
			s->as.u.clef.line =
				s->as.u.clef.type == TREBLE ? 2 : 4;
		}
		staff_clef[staff].clef = staff_tb[staff].s_clef = s;
	}

	for (s = tsfirst; s; s = s->ts_next) {
		for (g = s->extra ; g; g = g->next) {
			if (g->type == FMTCHG && g->u == REPEAT) {
				set_repeat(g, s);
				break;
			}
		}

		// handle %%staves
		if (s->sflags & S_NEW_SY) {
			sy = sy->next;
			for (staff = 0; staff <= nstaff; staff++)
				staff_clef[staff].autoclef = 1;
			for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
				voice = p_voice - voice_tb;
				if (sy->voice[voice].range < 0)
					continue;
				staff = sy->voice[voice].staff;
				if (!sy->voice[voice].second) {
					if (p_voice->stafflines >= 0)
						sy->staff[staff].stafflines =
								p_voice->stafflines;
					if (p_voice->staffscale != 0)
						sy->staff[staff].staffscale =
								p_voice->staffscale;
					if (sy->voice[voice].sep)
						sy->staff[staff].sep =
								sy->voice[voice].sep;
					if (sy->voice[voice].maxsep)
						sy->staff[staff].maxsep =
								sy->voice[voice].maxsep;
				}
				s2 = p_voice->s_clef;
				if (!(s2->sflags & S_CLEF_AUTO))
					staff_clef[staff].autoclef = 0;
			}
			for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
				voice = p_voice - voice_tb;
				if (sy->voice[voice].range < 0
				 || sy->voice[voice].second)
					continue;
				staff = sy->voice[voice].staff;
				s2 = p_voice->s_clef;
				if (s2->sflags & S_CLEF_AUTO) {
//fixme: the staff may have other voices with explicit clefs...
//					if (!staff_clef[staff].autoclef)
//						???
					new_type = set_auto_clef(staff, s,
						staff_clef[staff].clef ?
							staff_clef[staff].clef->as.u.clef.type :
							AUTOCLEF);
					new_line = new_type == TREBLE ? 2 : 4;
				} else {
					new_type = s2->as.u.clef.type;
					new_line = s2->as.u.clef.line;
				}
				if (!staff_clef[staff].clef) {	// new staff
					if (s2->sflags & S_CLEF_AUTO) {
						if (s2->as.u.clef.type != AUTOCLEF) {
							p_voice->s_clef =
								(struct SYMBOL *) getarena(sizeof *s);
							memcpy(p_voice->s_clef,
								s2,
								sizeof *p_voice->s_clef);
						}
						p_voice->s_clef->as.u.clef.type = new_type;
						p_voice->s_clef->as.u.clef.line = new_line;
					}
					staff_tb[staff].s_clef =
						staff_clef[staff].clef = p_voice->s_clef;
					continue;
				}
								// old staff
				if (new_type == staff_clef[staff].clef->as.u.clef.type
				 && new_line == staff_clef[staff].clef->as.u.clef.line)
					continue;
				g = s;
				while (g->voice != voice)
					g = g->ts_next;
				if (g->type != CLEF) {
					g = insert_clef(g, new_type, new_line);
					if (s2->sflags & S_CLEF_AUTO)
						g->sflags |= S_CLEF_AUTO;
				}
				staff_clef[staff].clef = p_voice->s_clef = g;
			}
		}
		if (s->type != CLEF)
			continue;
		p_voice = &voice_tb[s->voice];
		p_voice->s_clef = s;
		if (s->sflags & S_SECOND) {
/*fixme:%%staves:can this happen?*/
//			if (!s->prev)
//				break;
			unlksym(s);
			continue;
		}
		staff = s->staff;
// may have been inserted on %%staves
//		if (s->sflags & S_CLEF_AUTO) {
//			unlksym(s);
//			continue;
//		}

		if (staff_tb[staff].s_clef) {
			if (s->as.u.clef.type == staff_clef[staff].clef->as.u.clef.type
			 && s->as.u.clef.line == staff_clef[staff].clef->as.u.clef.line
			 && !(s->sflags & S_NEW_SY)) {
//				unlksym(s);
				continue;
			}
		} else {

			// the voice moved to a new staff with a forced clef
			 staff_tb[staff].s_clef = s;
		}
		staff_clef[staff].clef = s;
	}

	/* set a pitch to the symbols of voices with no note */
	sy = cursys;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		voice = p_voice - voice_tb;
		if (sy->voice[voice].range < 0)
			continue;
		s2 = p_voice->sym;
		if (!s2 || s2->pits[0] != 127)
			continue;
		staff = sy->voice[voice].staff;
		switch (staff_tb[staff].s_clef->as.u.clef.type) {
		default:
			pitch = 22;		/* 'B' */
			break;
		case ALTO:
			pitch = 16;		/* 'C' */
			break;
		case BASS:
			pitch = 10;		/* 'D,' */
			break;
		}
		for (s = s2; s; s = s->next)
			s->pits[0] = pitch;
	}

	lvlarena(old_lvl);
}

/* -- set the pitch of the notes according to the clefs -- */
/* also treat the auto clefs and set the vertical offset of the symbols */
/* this function is called only once per tune
 * then, once per music line up to the old sequence */
static void set_pitch(struct SYMBOL *last_s)
{
	struct SYMBOL *s, *g;
	int staff, delta, dur;
	signed char staff_delta[MAXSTAFF];
	static const signed char delta_tb[4] = {
		0 - 2 * 2,
		6 - 3 * 2,
		12 - 4 * 2,
		0 - 2 * 2
	};

	for (staff = 0; staff <= nstaff; staff++) {
		s = staff_tb[staff].s_clef;
		staff_delta[staff] = delta_tb[s->as.u.clef.type] +
				s->as.u.clef.line * 2 +
				s->as.u.clef.transpose;
	}

	dur = BASE_LEN;
	for (s = tsfirst; s != last_s; s = s->ts_next) {
		int np, m;

		staff = s->staff;
		switch (s->type) {
		case CLEF:
			staff_delta[staff] = delta_tb[s->as.u.clef.type] +
						s->as.u.clef.line * 2 +
						s->as.u.clef.transpose;
			set_yval(s);
			break;
		case GRACE:
			for (g = s->extra; g; g = g->next) {
				if (g->type != NOTEREST)
					continue;
				delta = staff_delta[g->staff];
				if (delta != 0) {
					for (m = g->nhd; m >= 0; m--)
						g->pits[m] += delta;
				}
				g->ymn = 3 * (g->pits[0] - 18) - 2;
				g->ymx = 3 * (g->pits[g->nhd] - 18) + 2;
			}
			set_yval(s);
			break;
		case KEYSIG:
			s->pits[0] = staff_delta[staff]; /* keep the current clef */
//			s->ymx = 24 + 10;
//			s->ymn = -2;
//			break;
			/* fall thru */
		default:
			set_yval(s);
			break;
		case MREST:
			if (s->as.flags & ABC_F_INVIS)
				break;
			s->ymx = 24 + 15;
			s->ymn = -2;
			break;
		case NOTEREST:
			if (s->as.type != ABC_T_NOTE
			 && !first_voice->next) {
				s->y = 12;		/* rest single voice */
				s->ymx = 12 + 8;
				s->ymn = 12 - 8;
				break;
			}
			np = s->nhd;
			delta = staff_delta[staff];
			if (delta != 0) {
				for (m = np; m >= 0; m--)
					s->pits[m] += delta;
			}
			s->ymx = 3 * (s->pits[np] - 18) + 4;
			s->ymn = 3 * (s->pits[0] - 18) - 4;
			s->yav = (s->ymx + s->ymn) / 2;
// test rest offset
			if (s->as.type != ABC_T_NOTE)
				s->y = s->yav / 6 * 6;
			if (s->dur < dur)
				dur = s->dur;
			break;
		}
	}
	smallest_duration = dur;
}

/* -- set the stem direction when multi-voices -- */
/* this function is called only once per tune */
static void set_stem_dir(void)
{
	struct SYSTEM *sy;
	struct SYMBOL *s, *t, *u;
	int i, staff, nst, rvoice, voice;
	struct {
		int nvoice;
		struct {
			int voice;
			short ymn;
			short ymx;
		} st[4];		/* (no more than 4 voices per staff) */
	} stb[MAXSTAFF];
	struct {
		signed char st1, st2;	/* (a voice cannot be on more than 2 staves) */
	} vtb[MAXVOICE];

	s = tsfirst;
	sy = cursys;
	nst = sy->nstaff;
	while (s) {
		for (staff = nst; staff >= 0; staff--) {
			stb[staff].nvoice = -1;
			for (i = 4; --i >= 0; ) {
				stb[staff].st[i].voice = -1;
				stb[staff].st[i].ymx = 0;
				stb[staff].st[i].ymn = 24;
			}
		}
		for (i = 0; i < MAXVOICE; i++)
			vtb[i].st1 = vtb[i].st2 = -1;

		/* get the max/min offsets in the delta time */
/*fixme: the stem height is not calculated yet*/
		for (u = s; u; u = u->ts_next) {
			if (u->type == BAR)
				break;
			if (u->sflags & S_NEW_SY) {
				if (u != s)
					break;
				sy = sy->next;
				for (staff = nst; staff <= sy->nstaff; staff++) {
					stb[staff].nvoice = -1;
					for (i = 4; --i >= 0; ) {
						stb[staff].st[i].voice = -1;
						stb[staff].st[i].ymx = 0;
						stb[staff].st[i].ymn = 24;
					}
				}
				nst = sy->nstaff;
			}
			if (u->type != NOTEREST
			 || (u->as.flags & ABC_F_INVIS))
				continue;
			staff = u->staff;
#if 1
/*fixme:test*/
if (staff > nst) {
	bug("set_stem_dir(): bad staff number\n", 1);
}
#endif
			voice = u->voice;
			if (vtb[voice].st1 < 0) {
				vtb[voice].st1 = staff;
			} else if (vtb[voice].st1 != staff) {
				if (staff > vtb[voice].st1) {
					if (staff > vtb[voice].st2)
						vtb[voice].st2 = staff;
				} else {
					if (vtb[voice].st1 > vtb[voice].st2)
						vtb[voice].st2 = vtb[voice].st1;
					vtb[voice].st1 = staff;
				}
			}
			rvoice = sy->voice[voice].range;
			for (i = stb[staff].nvoice; i >= 0; i--) {
				if (stb[staff].st[i].voice == rvoice)
					break;
			}
			if (i < 0) {
				if (++stb[staff].nvoice >= 4)
					bug("Too many voices per staff", 1);
				for (i = 0; i < stb[staff].nvoice; i++) {
					if (rvoice < stb[staff].st[i].voice) {
						memmove(&stb[staff].st[i + 1],
							&stb[staff].st[i],
							sizeof stb[staff].st[i]
								* (stb[staff].nvoice - i));
						stb[staff].st[i].ymx = 0;
						stb[staff].st[i].ymn = 24;
						break;
					}
				}
				stb[staff].st[i].voice = rvoice;
			}

			if (u->as.type != ABC_T_NOTE)
				continue;
			if (u->ymx > stb[staff].st[i].ymx)
				stb[staff].st[i].ymx = u->ymx;
			if (u->ymn < stb[staff].st[i].ymn)
				stb[staff].st[i].ymn = u->ymn;
			if (u->sflags & S_XSTEM) {
				if (u->ts_prev->staff != staff - 1
				 || u->ts_prev->as.type != ABC_T_NOTE) {
					error(1, s, "Bad +xstem+");
					u->sflags &= ~S_XSTEM;
/*fixme:nflags KO*/
				} else {
					u->ts_prev->multi = 1;
					u->multi = 1;
					u->as.flags |= ABC_F_STEMLESS;
				}
			}
		}

		for ( ; s != u; s = s->ts_next) {
//--fixme: multi change
			if (s->multi)
				continue;
			if (s->type != NOTEREST		/* if not note nor rest */
			 && s->type != GRACE)
				continue;
			staff = s->staff;
			voice = s->voice;
//			if (!s->multi && vtb[voice].st2 >= 0) {
			if (vtb[voice].st2 >= 0) {
				if (staff == vtb[voice].st1)
					s->multi = -1;
				else if (staff == vtb[voice].st2)
					s->multi = 1;
				continue;
			}
			if (stb[staff].nvoice <= 0) { /* voice alone on the staff */
//				if (s->multi)
//					continue;
/*fixme:could be done in set_float()*/
				if (s->sflags & S_FLOATING) {
					if (staff == voice_tb[voice].staff)
						s->multi = -1;
					else
						s->multi = 1;
				}
				continue;
			}
			rvoice = sy->voice[voice].range;
			for (i = stb[staff].nvoice; i >= 0; i--) {
				if (stb[staff].st[i].voice == rvoice)
					break;
			}
			if (i < 0)
				continue;		/* voice ignored */
			if (i == stb[staff].nvoice) {
				s->multi = -1;	/* last voice */
			} else {
				s->multi = 1;	/* first voice(s) */

				/* if 3 voices, and vertical space enough,
				 * have stems down for the middle voice */
				if (i != 0
				 && i + 1 == stb[staff].nvoice) {
					if (stb[staff].st[i].ymn - cfmt.stemheight
					    > stb[staff].st[i + 1].ymx)
						s->multi = -1;

					/* special case for unison */
					if (s->ts_prev
					 && s->ts_prev->time == s->time
					 && s->ts_prev->staff == s->staff
					 && s->pits[s->nhd] == s->ts_prev->pits[0]
					 && (s->sflags & (S_BEAM_ST | S_BEAM_END))
							== (S_BEAM_ST | S_BEAM_END)
					 && ((t = s->ts_next) == NULL
					  || t->staff != s->staff
					  || t->time != s->time))
						s->multi = -1;
				}
			}
		}

		while (s && s->type == BAR) {
			if (s->sflags & S_NEW_SY) {
				sy = sy->next;
				nst = sy->nstaff;
			}
			s = s->ts_next;
		}
	}
}

/* -- shift a rest vertically or horizontally -- */
static void shift_rest(struct SYMBOL *s,	/* rest */
			struct SYMBOL *s2,	/* other note/rest */
			struct SYSTEM *sy)
{
	int y, us, ls, ymx, ymn;

	us = rest_sp[C_XFLAGS - s->nflags].u;
	ls = rest_sp[C_XFLAGS - s->nflags].l;

	/* check if clash */
	ymx = s->y + us;
	ymn = s->y - ls;
	if (ymx < s2->ymn
	 || ymn > s2->ymx)
		return;			/* no */

	/* decide to move the rest upper or lower
	 * according to the voice ranges */
	if (sy->voice[s->voice].range > sy->voice[s2->voice].range)
		ymx = s2->ymn;			/* lower */
	else
		ymx = s2->ymx;			/* upper */

	/* change the rest vertical offset */
	if (ymx >= s2->ymx) {
		y = (s2->ymx + ls + 3) / 6 * 6;
		if (y < 12)
			y = 12;
		if (s->y < y)
			s->y = y;
	} else {
		y = (s2->ymn - us - 3) / 6 * 6;
		if (y > 12)
			y = 12;
		if (s->y > y)
			s->y = y;
	}
	s->ymx = s->y + us;
	s->ymn = s->y - ls;
}

/* -- adjust the offset of the rests when many voices -- */
/* this function is called only once per tune */
static void set_rest_offset(void)
{
	struct SYSTEM *sy;
	struct SYMBOL *s, *s2, *prev;
	int nvoice, voice, end_time, not_alone;
	struct {
		struct SYMBOL *s;
		int staff;
		int end_time;
	} vtb[MAXVOICE], *v;

	memset(vtb, 0, sizeof vtb);
	
	sy = cursys;
	nvoice = 0;
	for (s = tsfirst; s; s = s->ts_next) {
		if (s->as.flags & ABC_F_INVIS)
			continue;
		if (s->sflags & S_NEW_SY)
			sy = sy->next;
		if (s->type != NOTEREST)
			continue;
		if (s->voice > nvoice)
			nvoice = s->voice;
		v = &vtb[s->voice];
		v->s = s;
		v->staff = s->staff;
		v->end_time = s->time + s->dur;
		if (s->as.type != ABC_T_REST)
			continue;

		/* check if clash with previous symbols */
		not_alone = 0;
		prev = NULL;
		for (voice = 0, v = vtb; voice <= nvoice; voice++, v++) {
			if (!v->s
			 || v->staff != s->staff
			 || voice == s->voice)
				continue;
			if (v->end_time <= s->time)
				continue;
			not_alone++;
			shift_rest(s, v->s, sy);
			if (voice < s->voice && v->s->time == s->time)
				prev = v->s;
			break;
		}

		/* check if clash with next symbols */
		end_time = s->time + s->dur;
		for (s2 = s->ts_next; s2; s2 = s2->ts_next) {
			if (s2->time >= end_time)
				break;
			if (s2->staff != s->staff
			 || s2->type != NOTEREST
			 || (s2->as.flags & ABC_F_INVIS))
				continue;
			not_alone++;
			if (s2->as.type != ABC_T_REST) {
				shift_rest(s, s2, sy);

				/* shift to the right if same time and clash */
				if (prev
				 && s2->time == s->time
				 && (s2->ymx > s->ymn
				  || prev->ymn < s->ymx)) {
					int y;

					s->shhd[0] = 10;
					s->xmx = 10;
					y = (prev->ymn + s2->ymx) / 2;
					if (y < 12)
						y += 5;
					s->y = y / 6 * 6;
					s->ymx = s->y + 8;
					s->ymn = s->y - 8;
				}
			}
		}
		if (!not_alone) {
			s->y = 12;
			s->ymx = 12 + 8;
			s->ymn = 12 - 8;
		}
	}
}

/* -- create a starting symbol -- */
static struct SYMBOL *sym_new(int type,
				struct VOICE_S *p_voice,
				struct SYMBOL *last_s)	/* same time */
{
	struct SYMBOL *s;

	s = (struct SYMBOL *) getarena(sizeof *s);
	memset(s, 0, sizeof *s);
	s->type = type;
	s->voice = p_voice - voice_tb;
	s->staff = p_voice->staff;
	s->time = last_s->time;

	s->next = p_voice->last_sym->next;
	if (s->next)
		s->next->prev = s;
	p_voice->last_sym->next = s;
	s->prev = p_voice->last_sym;
	p_voice->last_sym = s;

	s->ts_next = last_s;
	s->ts_prev = last_s->ts_prev;
	s->ts_prev->ts_next = s;
	if (!s->ts_prev || s->ts_prev->type != type)
		s->sflags |= S_SEQST;
	last_s->ts_prev = s;
	if (last_s->type == type && s->voice != last_s->voice) {
		last_s->sflags &= ~S_SEQST;
		last_s->shrink = 0;
	}
	s->as.fn = last_s->as.fn;
	s->as.linenum = last_s->as.linenum;
	s->as.colnum = last_s->as.colnum;
	return s;
}

/* -- init the symbols at start of a music line -- */
/* this routine is called when starting a tune generation,
 * and later for each new music line */
static void init_music_line(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s, *last_s;
	int voice, staff;

	/* initialize the voices */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		voice = p_voice - voice_tb;
		if (cursys->voice[voice].range < 0)
			continue;
		p_voice->second = cursys->voice[voice].second;

		/* move the voice to a non empty staff */
		staff = cursys->voice[voice].staff;
		while (staff < nstaff && cursys->staff[staff].empty)
			staff++;
		p_voice->staff = staff;
	}

	/* add a clef at start of the main voices */
	last_s = tsfirst;
	while (last_s->type == CLEF) {		/* move the starting clefs */
		voice = last_s->voice;
		p_voice = &voice_tb[voice];
		if (cursys->voice[voice].range >= 0
		 && !cursys->voice[voice].second) {
			last_s->u = 0;			/* normal clef */
			p_voice->last_sym = p_voice->sym = last_s;
		}
		last_s = last_s->ts_next;
	}
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (p_voice->sym && p_voice->sym->type == CLEF)
			continue;
		voice = p_voice - voice_tb;
		if (cursys->voice[voice].range < 0
		 || cursys->voice[voice].second)
			continue;
		staff = cursys->voice[voice].staff;
#if 0
		if (last_s->voice == voice && last_s->type == CLEF) {
			last_s->u = 0;			/* normal clef */
#if 0
			if (cursys->staff[staff].clef.invis)
				s->as.flags |= ABC_F_INVIS;
#endif
			p_voice->last_sym = p_voice->sym = last_s;
			last_s = last_s->ts_next;
			continue;
		}
#endif
		if (!staff_tb[staff].s_clef)
			continue;			// no clef

		s = (struct SYMBOL *) getarena(sizeof *s);
		memset(s, 0, sizeof *s);
		memcpy(&s->as.u.clef, &staff_tb[staff].s_clef->as.u.clef,
				sizeof s->as.u.clef);
		s->type = CLEF;
		s->voice = voice;
		s->staff = staff;
		s->time = last_s->time;
		s->next = p_voice->sym;
		if (s->next) {
			s->next->prev = s;
			s->as.fn = s->next->as.fn;
			s->as.linenum = s->next->as.linenum;
			s->as.colnum = s->next->as.colnum;
		}
		p_voice->last_sym = p_voice->sym = s;
		s->ts_next = last_s;
		s->ts_prev = last_s->ts_prev;
		if (!s->ts_prev) {
			tsfirst = s;
			s->sflags |= S_SEQST;
		} else {
			s->ts_prev->ts_next = s;
		}
		last_s->ts_prev = s;
		if (last_s->type == CLEF)
			last_s->sflags &= ~S_SEQST;
//		if (cursys->voice[voice].second)
//			s->sflags |= S_SECOND;
		if (voice_tb[voice].s_clef->as.u.clef.invis
		 || cursys->staff[staff].empty)
			s->as.flags |= ABC_F_INVIS;
//		set_yval(s);
	}

	/* add keysig */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		voice = p_voice - voice_tb;
		if (cursys->voice[voice].range < 0
		 || cursys->voice[voice].second
		 || cursys->staff[cursys->voice[voice].staff].empty)
			continue;
		if (last_s->voice == voice && last_s->type == KEYSIG) {
			p_voice->last_sym = last_s;
			last_s = last_s->ts_next;
			continue;
		}
		if (p_voice->key.sf != 0 || p_voice->key.nacc != 0) {
			s = sym_new(KEYSIG, p_voice, last_s);
			memcpy(&s->as.u.key, &p_voice->key, sizeof s->as.u.key);
			if (s->as.u.key.mode == BAGPIPE + 1)
				s->u = 3;	/* K:Hp --> G natural */
//			set_yval(s);
		}
	}

	/* add time signature if needed */
	if (insert_meter & 1) {
		for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
			voice = p_voice - voice_tb;
			if (cursys->voice[voice].range < 0
			 || cursys->voice[voice].second
			 || cursys->staff[cursys->voice[voice].staff].empty
			 || p_voice->meter.nmeter == 0)		/* M:none */
				continue;
			if (last_s->voice == voice && last_s->type == TIMESIG) {
				p_voice->last_sym = last_s;
				last_s = last_s->ts_next;
				continue;
			}
			s = sym_new(TIMESIG, p_voice, last_s);
			memcpy(&s->as.u.meter, &p_voice->meter,
			       sizeof s->as.u.meter);
//			set_yval(s);
		}
	}

	/* add bar if needed */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		int i;

		if (p_voice->bar_start == 0)
			continue;
		voice = p_voice - voice_tb;
		if (cursys->voice[voice].range < 0
		 || cursys->voice[voice].second
		 || cursys->staff[cursys->voice[voice].staff].empty)
			continue;
		i = 2;
		if (!p_voice->bar_text		/* if repeat continuation */
		 && p_voice->bar_start == B_OBRA) {
			for (s = p_voice->last_sym;
			     s;
			     s = s->next) {	/* search the end of repeat */
				if (s->sflags & S_RBSTOP) {
					i = -1;
					break;
				}
				if (s->type != BAR)
					continue;
				if ((s->as.u.bar.type & 0xf0)	/* if complex bar */
				 || s->as.u.bar.type == B_CBRA
				 || s->as.u.bar.repeat_bar)
					break;
				if (--i < 0)
					break;
			}
			if (!s)
				i = -1;
			if (i >= 0 && p_voice->last_sym->time == s->time)
				i = -1;		/* no note */
		}
		if (i >= 0) {
			s = sym_new(BAR, p_voice, last_s);
			s->as.u.bar.type = p_voice->bar_start & 0x3fff;
			if (p_voice->bar_start & 0x8000)
				s->as.flags |= ABC_F_INVIS;
			if (p_voice->bar_start & 0x4000)
				s->sflags |= S_NOREPBRA;
			s->as.text = p_voice->bar_text;
			s->gch = p_voice->bar_gch;
			s->as.u.bar.repeat_bar = p_voice->bar_repeat;
//			set_yval(s);
		}
		p_voice->bar_start = 0;
		p_voice->bar_repeat = 0;
		p_voice->bar_text = 0;
		p_voice->bar_gch = 0;
	}

	/* if initialization of a new music line, compute the spacing,
	 * including the first (old) sequence */
	set_pitch(last_s);
	s = last_s;
	if (s) {
		for ( ; s; s = s->ts_next)
			if (s->sflags & S_SEQST)
				break;
		if (s)
		    for (s = s->ts_next; s; s = s->ts_next)
			if (s->sflags & S_SEQST)
				break;
	}
	set_allsymwidth(s);	/* set the width of the added symbols */
}

/* -- initialize the generator -- */
/* this function is called only once per tune  */
static void set_global(void)
{
	struct SYSTEM *sy;
	struct SYMBOL *s;
	struct VOICE_S *p_voice;
	int staff;
	static const signed char delpit[4] = {0, -7, -14, 0};

	/* get the max number of staves */
	sy = cursys;
	staff = cursys->nstaff;
	while ((sy = sy->next) != NULL) {
		if (sy->nstaff > staff)
			staff = sy->nstaff;
	}
	nstaff = staff;

	/* adjust the pitches if old abc2ps behaviour of clef definition */
	if (cfmt.abc2pscompat) {
		int i;

		for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
			int delta;
			struct SYMBOL *g;

			if (p_voice->octave != 0)
				continue;
#if 0
			/* (the clefs in the voice table are not yet initialized) */
//			i = p_voice->staff;
//			i = cursys->staff[i].clef.type;
			i = cursys->voice[p_voice - voice_tb].clef.type;
#else
			i = p_voice->s_clef->as.u.clef.type;
#endif
			if (i == PERC)
				continue;
			delta = delpit[i];
			for (s = p_voice->sym; s; s = s->next) {
				switch (s->type) {
				case CLEF:
					i = s->as.u.clef.type;
					if (!s->as.u.clef.check_pitch)
						i = 0;
					delta = delpit[i];
					break;
				case NOTEREST:
					if (delta == 0)
						break;
					if (s->as.type == ABC_T_REST)
						break;
					for (i = s->nhd; i >= 0; i--)
						s->pits[i] += delta;
					break;
				case GRACE:
					if (delta == 0)
						break;
					for (g = s->extra; g; g = g->next) {
						if (g->type != NOTEREST)
							continue;
						for (i = g->nhd; i >= 0; i--)
							g->pits[i] += delta;
					}
					break;
				}
			}
		}
	}

	/* set a pitch for all symbols and the start/stop of words (beams) */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		int pitch, start_flag;
		struct SYMBOL *sym, *lastnote;

		sym = p_voice->sym;
		for (s = sym; s; s = s->next) {
			if (s->as.type == ABC_T_NOTE) {
				pitch = s->pits[0];
				break;
			}
		}
		if (!s)
			pitch = 127;			/* no note */
		start_flag = 1;
		lastnote = 0;
		for (s = sym; s; s = s->next) {
			switch (s->type) {
			default:
				if (s->as.flags & ABC_F_SPACE)
					start_flag = 1;
				break;
			case MREST:
				start_flag = 1;
				break;
			case BAR:
				if (!(s->sflags & S_BEAM_ON))
					start_flag = 1;
				if (!s->next && s->prev
				 && s->prev->as.type == ABC_T_NOTE
				 && s->prev->dur >= BREVE)
					s->prev->head = H_SQUARE;
				break;
			case NOTEREST:
				if (s->sflags & S_TREM2)
					break;
				if (s->as.flags & ABC_F_SPACE)
					start_flag = 1;
				if (start_flag
				 || s->nflags - s->u <= 0) {
					if (lastnote) {
						lastnote->sflags |= S_BEAM_END;
						lastnote = NULL;
					}
					if (s->nflags - s->u <= 0) {
						s->sflags |= (S_BEAM_ST | S_BEAM_END);
					} else if (s->as.type == ABC_T_NOTE) {
						s->sflags |= S_BEAM_ST;
						start_flag = 0;
					}
				}
				if (s->sflags & S_BEAM_END)
					start_flag = 1;
				if (s->as.type == ABC_T_NOTE)
					lastnote = s;
				break;
			}
			if (s->as.type == ABC_T_NOTE) {
				pitch = s->pits[0];
				if (s->prev
				 && s->prev->as.type != ABC_T_NOTE) {
					s->prev->pits[0] = (s->prev->pits[0]
							    + pitch) / 2;
				}
			} else {
				s->pits[0] = pitch;
			}
		}
		if (lastnote)
			lastnote->sflags |= S_BEAM_END;
	}

	/* set the staff of the floating voices */
	set_float();

	// set the clefs and adjust the pitches of all symbols
	set_clefs();
	set_pitch(NULL);
}

/* -- return the left indentation of the staves -- */
static float set_indent(void)
{
	int staff, voice;
	float w, maxw;
	struct VOICE_S *p_voice;
	char *p, *q;

	maxw = 0;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		voice = p_voice - voice_tb;
		if (cursys->voice[voice].range < 0)
			continue;
		staff = cursys->voice[voice].staff;
		if (cursys->staff[staff].empty)
			continue;
		if ((p = p_voice->new_name ? p_voice->nm : p_voice->snm) == NULL)
			continue;
		str_font(VOICEFONT);
		for (;;) {
			if ((q = strstr(p, "\\n")) != NULL)
				*q = '\0';
			w = tex_str(p);
			if (w > maxw)
				maxw = w;
			if (!q)
				break;
			*q = '\\';
			p = q + 2;
		}
	}

	if (maxw != 0) {
		w = 0;
//		for (staff = 0; staff <= nstaff; staff++) {
		for (staff = 0; staff <= cursys->nstaff; staff++) {
			if (cursys->staff[staff].flags
					& (OPEN_BRACE2 | OPEN_BRACKET2)) {
				w = 20;
				break;
			}
			if ((cursys->staff[staff].flags
					& (OPEN_BRACE | OPEN_BRACKET))
			 && w == 0)
				w = 10;
		}
		maxw += 4 * cwid(' ') * cfmt.font_tb[VOICEFONT].swfac + w;
	}
	if (insert_meter & 2)			/* if indent */
		maxw += cfmt.indent;
	return maxw;
}

/* -- decide on beams and on stem directions -- */
/* this routine is called only once per tune */
static void set_beams(struct SYMBOL *sym)
{
	struct SYMBOL *s, *t, *g, *s_opp;
	int beam, laststem, lasty;

	beam = 0;
	laststem = -1;
	lasty = 0;
	s_opp = NULL;
	for (s = sym; s; s = s->next) {
		if (s->as.type != ABC_T_NOTE) {
			if (s->type != GRACE)
				continue;
			g = s->extra;
			while (g->as.type != ABC_T_NOTE)
				g = g->next;
			if (g->stem == 2) {	/* opposite gstem direction */
				s_opp = s;
				continue;
			}
			if (s->stem == 0
			 && (s->stem = s->multi) == 0)
				s->stem = 1;
			for (; g; g = g->next) {
				g->stem = s->stem;
				g->multi = s->multi;
			}
			continue;
		}

		if (s->stem == 0		/* if not explicitly set */
		 && (s->stem = s->multi) == 0) { /* and alone on the staff */

			/* notes in a beam have the same stem direction */
			if (beam) {
				s->stem = laststem;
			} else if ((s->sflags & (S_BEAM_ST | S_BEAM_END))
					== S_BEAM_ST) { /* start of beam */
				int avg, n;

				avg = s->yav;
				n = 12;
				for (t = s->next; t; t = t->next) {
					if (t->as.type == ABC_T_NOTE) {
						if (t->multi) {
							avg = n - t->multi;
							break;
						}
						avg += t->yav;
						n += 12;
					}
					if (t->sflags & S_BEAM_END)
						break;
				}
				if (avg < n)
					laststem = 1;
				else if (avg > n || cfmt.bstemdown)
					laststem = -1;
				beam = 1;
				s->stem = laststem;
			} else {
				s->stem = s->yav >= 12 ? -1 : 1;
				if (s->yav == 12	/* note on middle line */
				 && !cfmt.bstemdown) {
					int dy;

					if (!s->prev || s->prev->type == BAR) {
						for (t = s->next; t; t = t->next) {
							if (t->as.type == ABC_T_NOTE
							 || t->type == BAR)
								break;
						}
						if (t && t->as.type == ABC_T_NOTE
						 && t->yav < 12)
							s->stem = 1;
					} else {
						dy = s->yav - lasty;
						if (dy > -7 && dy < 7)
							s->stem = laststem;
					}
				}
			}
		} else {			/* stem set by set_stem_dir */
			if ((s->sflags & (S_BEAM_ST | S_BEAM_END))
					== S_BEAM_ST) /* start of beam */
				beam = 1;
		}
		if (s->sflags & S_BEAM_END)
			beam = 0;
		laststem = s->stem;
		lasty = s->yav;

		if (s_opp) {			/* opposite gstem direction */
			for (g = s_opp->extra; g; g = g->next)
				g->stem = -laststem;
			s_opp->stem = -laststem;
			s_opp = NULL;
		}
	}
}

/* handle unison in voice overlap */
static int same_head(struct SYMBOL *s1, struct SYMBOL *s2)
{
	int i1, i2, l1, l2, i11, i12, i21, i22;
	float sh1, sh2;

	if ((s1->sflags & (S_SHIFTUNISON_1 | S_SHIFTUNISON_2))
			== (S_SHIFTUNISON_1 | S_SHIFTUNISON_2))
		return 0;
	if ((l1 = s1->dur) >= SEMIBREVE)
		return 0;
	if ((l2 = s2->dur) >= SEMIBREVE)
		return 0;
	if (s1->as.flags & s2->as.flags & ABC_F_STEMLESS)
		return 0;
	if (s1->dots != s2->dots) {
		if ((s1->sflags & (S_SHIFTUNISON_1 | S_SHIFTUNISON_2))
		 || s1->dots * s2->dots != 0)
			return 0;
	}
	if (s1->stem * s2->stem > 0)
		return 0;

	/* check if a common unison */
	i1 = i2 = 0;
	if (s1->pits[0] > s2->pits[0]) {
		if (s1->stem < 0)
			return 0;
		while (s2->pits[i2] != s1->pits[0]) {
			if (++i2 > s2->nhd)
				return 0;
		}
	} else if (s1->pits[0] < s2->pits[0]) {
		if (s2->stem < 0)
			return 0;
		while (s2->pits[0] != s1->pits[i1]) {
			if (++i1 > s1->nhd)
				return 0;
		}
	}
	if (s2->as.u.note.accs[i2] != s1->as.u.note.accs[i1])
		return 0;
	i11 = i1;
	i21 = i2;
	sh1 = s1->shhd[i1];
	sh2 = s2->shhd[i2];
	do {
		i1++;
		i2++;
		if (i1 > s1->nhd) {
//			if (s1->pits[0] < s2->pits[0])
//				return 0;
			break;
		}
		if (i2 > s2->nhd) {
//			if (s1->pits[0] > s2->pits[0])
//				return 0;
			break;
		}
		if (s2->as.u.note.accs[i2] != s1->as.u.note.accs[i1])
			return 0;
		if (sh1 < s1->shhd[i1])
			sh1 = s1->shhd[i1];
		if (sh2 < s2->shhd[i2])
			sh2 = s2->shhd[i2];
	} while (s2->pits[i2] == s1->pits[i1]);
	if (i1 <= s1->nhd) {
		if (i2 <= s2->nhd)
			return 0;
		if (s2->stem > 0)
			return 0;
	} else if (i2 <= s2->nhd) {
		if (s1->stem > 0)
			return 0;
	}
	i12 = i1;
	i22 = i2;

	if (l1 == l2)
		goto same_head;
	if (l1 < l2) {
		l1 = l2;
		l2 = s1->dur;
	}
	if (l1 < MINIM) {
		if (s2->dots > 0)
			goto head_2;
		if (s1->dots > 0)
			goto head_1;
		goto same_head;
	}
	if (l2 < CROTCHET) {	/* (l1 >= MINIM) */
		if ((s1->sflags & S_SHIFTUNISON_2)
		 || s1->dots != s2->dots)
			return 0;
		if (s2->dur >= MINIM)
			goto head_2;
		goto head_1;
	}
	return 0;

same_head:
	if (voice_tb[s1->voice].scale < voice_tb[s2->voice].scale)
		goto head_2;
head_1:
	s2->nohdi1 = i21;	/* keep heads of 1st voice */
	s2->nohdi2 = i22;
//	for (; i2 <= s2->nhd; i2++)
//		s2->as.u.note.accs[i2] = 0;
	for (i2 = 0; i2 <= s2->nhd; i2++)
		s2->shhd[i2] += sh1;
	return 1;
head_2:
	s1->nohdi1 = i11;	/* keep heads of 2nd voice */
	s1->nohdi2 = i12;
//	for (; i1 >= 0; i1--)
//		s1->as.u.note.accs[i1] = 0;
	for (i1 = 0; i1 <= s1->nhd; i1++)
		s1->shhd[i1] += sh2;
	return 1;
}

/* width of notes for voice overlap - index = head */
static float w_note[] = {
	3.5, 3.5, 5, 7
};

/* handle unison with different accidentals */
static void unison_acc(struct SYMBOL *s1,
			struct SYMBOL *s2,
			int i1, int i2)
{
	int m;
	float d;

	if (s2->as.u.note.accs[i2] == 0) {
		d = w_note[s2->head] * 2 + s2->xmx + s1->shac[i1] + 2;
		if (s1->as.u.note.accs[i1] & 0xf8)
			d += 2;
		if (s2->dots)
			d += 6;
		for (m = 0; m <= s1->nhd; m++) {
			s1->shhd[m] += d;
			s1->shac[m] -= d;
		}
		s1->xmx += d;
	} else {
		d = w_note[s1->head] * 2 + s1->xmx + s2->shac[i2] + 2;
		if (s2->as.u.note.accs[i2] & 0xf8)
			d += 2;
		if (s1->dots)
			d += 6;
		for (m = 0; m <= s2->nhd; m++) {
			s2->shhd[m] += d;
			s2->shac[m] -= d;
		}
		s2->xmx += d;
	}
}

#define MAXPIT (48 * 2)
/* set the left space of a note/chord */
static void set_left(struct SYMBOL *s, float *left)
{
	int m, i, j;
	float w_base, w, shift;

	w_base = w_note[s->head];
	for (i = 0; i < MAXPIT; i++)
		left[i] = -100;

	/* stem */
	w = w_base;
	if (s->stem > 0) {
		w = -w;
		i = s->pits[0] * 2;
		j = ((int) ((s->ymx - 2) / 3) + 18) * 2;
	} else {
		i = ((int) ((s->ymn + 2) / 3) + 18) * 2;
		j = s->pits[s->nhd] * 2;
	}
	if (i < 0)
		i = 0;
	for (; i < MAXPIT && i <= j; i++)
		left[i] = w;

	/* notes */
	if (s->stem > 0)
		shift = s->shhd[0];		/* previous shift */
	else
		shift = s->shhd[s->nhd];
	for (m = 0; m <= s->nhd; m++) {
		w = -s->shhd[m] + w_base + shift;
		i = s->pits[m] * 2;
		if (i < 0)
			i = 0;
		else if (i >= MAXPIT - 1)
			i = MAXPIT - 2;
		if (w > left[i])
			left[i] = w;
		if (s->head != H_SQUARE)
			w -= 1;
		if (w > left[i - 1])
			left[i - 1] = w;
		if (w > left[i + 1])
			left[i + 1] = w;
	}
}

/* set the right space of a note/chord */
static void set_right(struct SYMBOL *s, float *right)
{
	int m, i, j, k, flags;
	float w_base, w, shift;

	w_base = w_note[s->head];
	for (i = 0; i < MAXPIT; i++)
		right[i] = -100;

	/* stem and flags */
	flags = s->nflags > 0
	     && (s->sflags & (S_BEAM_ST | S_BEAM_END))
			== (S_BEAM_ST | S_BEAM_END);
	w = w_base;
	if (s->stem < 0) {
		w = -w;
		i = ((int) ((s->ymn + 2) / 3) + 18) * 2;
		j = s->pits[s->nhd] * 2;
		k = i + 4;
	} else {
		i = s->pits[0] * 2;
		j = ((int) ((s->ymx - 2) / 3) + 18) * 2;
		k = i;				// (have gcc happy)
	}
	if (i < 0)
		i = 0;
	for ( ; i < MAXPIT && i < j; i++)
		right[i] = w;
	if (flags) {
		if (s->stem > 0) {
			if (s->xmx == 0)
				i = s->pits[s->nhd] * 2;
			else
				i = s->pits[0] * 2;
			i += 4;
			if (i < 0)
				i = 0;
			for (; i < MAXPIT && i <= j - 4; i++)
				right[i] = 11;
		} else {
			i = k;
			if (i < 0)
				i = 0;
			for (; i < MAXPIT && i <= s->pits[0] * 2 - 4; i++)
				right[i] = 3.5;
		}
	}

	/* notes */
	if (s->stem > 0)
		shift = s->shhd[0];		/* previous shift */
	else
		shift = s->shhd[s->nhd];
	for (m = 0; m <= s->nhd; m++) {
		w = s->shhd[m] + w_base - shift;
		i = s->pits[m] * 2;
		if (i < 0)
			i = 0;
		else if (i >= MAXPIT - 1)
			i = MAXPIT - 2;
		if (w > right[i])
			right[i] = w;
		if (s->head != H_SQUARE)
			w -= 1;
		if (w > right[i - 1])
			right[i - 1] = w;
		if (w > right[i + 1])
			right[i + 1] = w;
	}
}

/* -- shift the notes horizontally when voices overlap -- */
/* this routine is called only once per tune */
static void set_overlap(void)
{
	struct SYMBOL *s, *s1, *s2;
	int i, i1, i2, m, sd, t, dp;
	float d, d2, dr, dr2, dx;
	float left1[MAXPIT], right1[MAXPIT], left2[MAXPIT], right2[MAXPIT];
	float *pl, *pr;

	for (s = tsfirst; s; s = s->ts_next) {
		if (s->as.type != ABC_T_NOTE
		 || (s->as.flags & ABC_F_INVIS))
			continue;

		/* treat the stem on two staves with different directions */
		if ((s->sflags & S_XSTEM)
		 && s->ts_prev->stem < 0) {
			s2 = s->ts_prev;
			for (m = 0; m <= s2->nhd; m++) {
				s2->shhd[m] += STEM_XOFF * 2;
				s2->shac[m] -= STEM_XOFF * 2;
			}
			s2->xmx += STEM_XOFF * 2;
		}

		/* search the next note at the same time on the same staff */
		s2 = s;
		for (;;) {
			s2 = s2->ts_next;
			if (!s2)
				break;
			if (s2->time != s->time) {
				s2 = NULL;
				break;
			}
			if (s2->as.type == ABC_T_NOTE
			 && !(s2->as.flags & ABC_F_INVIS)
			 && s2->staff == s->staff)
				break;
		}
		if (!s2)
			continue;
		s1 = s;

		/* set the dot vertical offset */
		if (cursys->voice[s1->voice].range < cursys->voice[s2->voice].range)
			s2->doty = -3;
		else
			s1->doty = -3;

		/* no shift if no overlap */
		d = 0;
		sd = 0;
		if (s1->ymn > s2->ymx
		 || s1->ymx < s2->ymn)
			goto acc_shift;

		if (same_head(s1, s2))
//			goto acc_shift;
			continue;

#if 0
		/* check simple unison */
		if (s1->pits[0] == s2->pits[s2->nhd]
		 || s2->pits[0] == s1->pits[s1->nhd]) {
			if (s1->pits[0] == s2->pits[s2->nhd]) {
				if (s1->as.u.note.accs[0] !=
						s2->as.u.note.accs[s2->nhd]) {
					unison_acc(s1, s2, 0, s2->nhd);
					continue;
				}
			} else {
				if (s2->as.u.note.accs[0] !=
						s1->as.u.note.accs[s1->nhd]) {
					unison_acc(s2, s1, 0, s1->nhd);
					continue;
				}
			}
			if (same_head(s1, s2))
				goto acc_shift;
		}
#endif

		/* compute the minimum space for 's1 s2' and 's2 s1' */
		set_right(s1, right1);
		set_left(s2, left2);
		d = -100;
		for (i = 0; i < MAXPIT; i++) {
			if (left2[i] + right1[i] > d)
				d = left2[i] + right1[i];
		}
		if (d < 0) {			// no clash if no dots clash
			if (!s1->dots || !s2->dots
			 || s2->doty >= 0
			 || s1->stem > 0 || s2->stem < 0
			 || s1->pits[s1->nhd] + 2 != s2->pits[0]
			 || (s2->pits[0] & 1))
				goto acc_shift;
		}

		set_right(s2, right2);
		set_left(s1, left1);
		d2 = dr = dr2 = -100;
		for (i = 0; i < MAXPIT; i++) {
			if (left1[i] + right2[i] > d2)
				d2 = left1[i] + right2[i];
			if (right1[i] > dr)
				dr = right1[i];
			if (right2[i] > dr2)
				dr2 = right2[i];
		}

		/* check for unison with different accidentals
		 * and clash of dots */
		t = 0;
		i1 = s1->nhd;
		i2 = s2->nhd;
		for (;;) {
			dp = s1->pits[i1] - s2->pits[i2];
			switch (dp) {
			case 0:
				if (s1->as.u.note.accs[i1]
						!= s2->as.u.note.accs[i2]) {
					t = -1;
					break;
				}
				t = 1;
				break;
			case -1:
//				if (s1->dots && s2->dots)
//					t = 1;
				if (s1->dots && s2->dots) {
					if (s1->pits[i1] & 1) {
						s1->doty = 0;
						s2->doty = 0;
					} else {
						s1->doty = -3;
						s2->doty = -3;
					}
				}
				break;
			case -2:
				if (s1->dots && s2->dots
				 && !(s1->pits[i1] & 1)) {
//					t = 1;
					s1->doty = 0;
					s2->doty = 0;
					break;
				}
				break;
			}
			if (t < 0)
				break;
			if (dp >= 0) {
				if (--i1 < 0)
					break;
			}
			if (dp <= 0) {
				if (--i2 < 0)
					break;
			}
		}
		
		if (t < 0) {		/* unison and different accidentals */
			unison_acc(s1, s2, i1, i2);
			continue;
		}

		pl = left2;
		pr = right2;
		if (s1->dots) {
			if (s2->dots) {
				if (!t)			/* if no dot clash */
					sd = 1;		/* align the dots */
			}
		} else if (s2->dots) {
			if (d2 + dr < d + dr2)
				sd = 1;			/* align the dots */
		}
		if (d2 + dr < d + dr2) {
			s1 = s2;			/* invert the voices */
			s2 = s;
			d = d2;
			pl = left1;
			pr = right1;
			dr2 = dr;
		}
		d += 3;
		if (d < 0)
			d = 0;				// (not return!)

		/* handle the previous shift */
		m = s1->stem >= 0 ? 0 : s1->nhd;
		d += s1->shhd[m];
		m = s2->stem >= 0 ? 0 : s2->nhd;
		d -= s2->shhd[m];

		/*
		 * room for the dots
		 * - if the dots of v1 don't shift, adjust the shift of v2
		 * - otherwise, align the dots and shift them if clash
		 */
		if (s1->dots) {
			dx = 7.7 + s1->xmx +		// x 1st dot
				3.5 * s1->dots - 3.5 +	// x last dot
				3;			// some space
			if (!sd) {
				d2 = -100;
				for (i1 = 0; i1 <= s1->nhd; i1++) {
					i = s1->pits[i1];
					if (!(i & 1)) {
						if (s1->doty >= 0)
							i++;
						else
							i--;
					}
					i *= 2;
					if (i < 1)
						i = 1;
					else if (i >= MAXPIT - 1)
						i = MAXPIT - 2;
					if (pl[i] > d2)
						d2 = pl[i];
					if (pl[i - 1] + 1 > d2)
						d2 = pl[i - 1] + 1;
					if (pl[i + 1] + 1 > d2)
						d2 = pl[i + 1] + 1;
				}
				if (dx + d2 + 2 > d)
					d = dx + d2 + 2;
			} else {
				if (dx < d + dr2 + s2->xmx) {
					d2 = 0;
					for (i1 = 0; i1 <= s1->nhd; i1++) {
						i = s1->pits[i1];
						if (!(i & 1)) {
							if (s1->doty >= 0)
								i++;
							else
								i--;
						}
						i *= 2;
						if (i < 1)
							i = 1;
						else if (i >= MAXPIT - 1)
							i = MAXPIT - 2;
						if (pr[i] > d2)
							d2 = pr[i];
						if (pr[i - 1] + 1> d2)
							d2 = pr[i - 1] = 1;
						if (pr[i + 1] + 1 > d2)
							d2 = pr[i + 1] + 1;
					}
					if (d2 > 4.5
					 && 7.7 + s1->xmx + 2 < d + d2 + s2->xmx)
						s2->xmx = d2 + 3 - 7.7;
				}
			}
		}

		for (m = s2->nhd; m >= 0; m--) {
			s2->shhd[m] += d;
//			if (s2->as.u.note.accs[m] != 0
//			 && s2->pits[m] < s1->pits[0] - 4)
//				s2->shac[m] -= d;
		}
		s2->xmx += d;
		if (sd)
			s1->xmx = s2->xmx;	// align the dots

		/* shift the accidentals */
	acc_shift:
		for (i1 = 0; i1 <= s1->nhd; i1++) {
			if (s1->as.u.note.accs[i1] == 0)
				continue;
			for (i2 = 0; i2 <= s2->nhd; i2++) {
				dp = s1->pits[i1] - s2->pits[i2];
//				if (dp > 5 || dp < -5)
//					continue;
				if (s2->as.u.note.accs[i2] == 0) {
					if (s2->shhd[i2] < 0
					 && dp == 3)
						s1->shac[i1] = 9 + 7;
					continue;
				}
				if (dp == 0) {
//					s2->as.u.note.accs[i2] = 0;
					continue;
				}
				dx = (dp <= -4 || dp >= 4) ? 4.5 : 7;
				if (dp > 0) {
					if (s1->as.u.note.accs[i1] & 0xf8)
						dx += 2;
					if (s2->shac[i2] < s1->shac[i1] + dx
					 && s2->shac[i2] > s1->shac[i1] - dx)
						s2->shac[i2] = s1->shac[i1] + dx;
				} else {
					if (s2->as.u.note.accs[i2] & 0xf8)
						dx += 2;
					if (s1->shac[i1] < s2->shac[i2] + dx
					 && s1->shac[i1] > s2->shac[i2] - dx)
						s1->shac[i1] = s2->shac[i2] + dx;
				}
			}
		}
	}
}

/* -- set the stem lengths -- */
/* this routine is called only once per tune */
static void set_stems(void)
{
	struct SYMBOL *s, *s2, *g;
	float slen, scale;
	int ymn, ymx, nflags;

	for (s = tsfirst; s; s = s->ts_next) {
		if (s->as.type != ABC_T_NOTE) {
			int ymin, ymax;

			if (s->type != GRACE)
				continue;
			ymin = ymax = 12;
			for (g = s->extra; g; g = g->next) {
				if (g->type != NOTEREST)
					continue;
				slen = GSTEM;
				if (g->nflags > 1)
					slen += 1.2 * (g->nflags - 1);
				ymn = 3 * (g->pits[0] - 18);
				ymx = 3 * (g->pits[g->nhd] - 18);
				if (s->stem >= 0) {
					g->y = ymn;
					g->ys = ymx + slen;
					ymx = (int) (g->ys + 0.5);
				} else {
					g->y = ymx;
					g->ys = ymn - slen;
					ymn = (int) (g->ys - 0.5);
				}
				ymx += 2;
				ymn -= 2;
				if (ymn < ymin)
					ymin = ymn;
				else if (ymx > ymax)
					ymax = ymx;
				g->ymx = ymx;
				g->ymn = ymn;
			}
			s->ymx = ymax;
			s->ymn = ymin;
			continue;
		}

		/* shift notes in chords (need stem direction to do this) */
		set_head_directions(s);

		/* if start or end of beam, adjust the number of flags
		 * with the other end */
		nflags = s->nflags;
		if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST) {
			if (s->sflags & S_FEATHERED_BEAM)
				nflags = ++s->nflags;
			for (s2 = s->next; /*s2*/; s2 = s2->next) {
				if (s2->as.type == ABC_T_NOTE) {
					if (s->sflags & S_FEATHERED_BEAM)
						s2->nflags++;
					if (s2->sflags & S_BEAM_END)
						break;
				}
			}
/*			if (s2) */
			    if (s2->nflags > nflags)
				nflags = s2->nflags;
		} else if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_END) {
			for (s2 = s->prev; /*s2*/; s2 = s2->prev) {
				if (s2->sflags & S_BEAM_ST)
					break;
			}
/*			if (s2) */
			    if (s2->nflags > nflags)
				nflags = s2->nflags;
		}

		/* set height of stem end */
		slen = cfmt.stemheight;
		switch (nflags) {
		case 2: slen += 2; break;
		case 3:	slen += 5; break;
		case 4:	slen += 10; break;
		case 5:	slen += 16; break;
		}
		if ((scale = voice_tb[s->voice].scale) != 1)
			slen *= (scale + 1) * 0.5;
		ymn = 3 * (s->pits[0] - 18);
		if (s->nhd > 0) {
			slen -= 2;
			ymx = 3 * (s->pits[s->nhd] - 18);
		} else {
			ymx = ymn;
		}
		if (s->u != 0)
			slen += 2 * s->u;		/* tremolo */
		if (s->as.flags & ABC_F_STEMLESS) {
			if (s->stem >= 0) {
				s->y = ymn;
				s->ys = ymx;
			} else {
				s->ys = ymn;
				s->y = ymx;
			}
			if (nflags == -4)		/* if longa */
				ymn -= 6;
			s->ymx = ymx + 4;
			s->ymn = ymn - 4;
		} else if (s->stem >= 0) {
			if (nflags >= 2)
				slen -= 1;
			if (s->pits[s->nhd] > 26
			 && (nflags <= 0
			  || (s->sflags & (S_BEAM_ST | S_BEAM_END))
					!= (S_BEAM_ST | S_BEAM_END))) {
				slen -= 2;
				if (s->pits[s->nhd] > 28)
					slen -= 2;
			}
			s->y = ymn;
			if (s->as.u.note.ti1[0] != 0)
/*fixme
 *			 || s->as.u.note.ti2[0] != 0) */
				ymn -= 3;
			s->ymn = ymn - 4;
			s->ys = ymx + slen;
			if (s->ys < 12)
				s->ys = 12;
			s->ymx = (int) (s->ys + 2.5);
		} else {			/* stem down */
			if (s->pits[0] < 18
			 && (nflags <= 0
			  || (s->sflags & (S_BEAM_ST | S_BEAM_END))
					!= (S_BEAM_ST | S_BEAM_END))) {
				slen -= 2;
				if (s->pits[0] < 16)
					slen -= 2;
			}
			s->ys = ymn - slen;
			if (s->ys > 12)
				s->ys = 12;
			s->ymn = (int) (s->ys - 2.5);
			s->y = ymx;
/*fixme:the tie may be lower*/
			if (s->as.u.note.ti1[s->nhd] != 0)
				ymx += 3;
			s->ymx = ymx + 4;
		}
	}
}

/* -- split up unsuitable bars at end of staff -- */
static void check_bar(struct SYMBOL *s)
{
	struct VOICE_S *p_voice;
	int bar_type, i;

	p_voice = &voice_tb[s->voice];

	/* search the last bar */
	while (s->type == CLEF || s->type == KEYSIG || s->type == TIMESIG) {
		if (s->type == TIMESIG
		 && s->time > p_voice->sym->time)	/* if not empty voice */
			insert_meter |= 1;	/* meter in the next line */
		if ((s = s->prev) == 0)
			return;
	}
	if (s->type != BAR)
		return;

	if (s->as.u.bar.repeat_bar) {
		p_voice->bar_start = B_OBRA;
		p_voice->bar_text = s->as.text;
		p_voice->bar_gch = s->gch;
		p_voice->bar_repeat = 1;
		s->as.text = NULL;
		s->gch = NULL;
		s->as.u.bar.repeat_bar = 0;
		if (s->as.flags & ABC_F_INVIS)
			p_voice->bar_start |= 0x8000;
		if (s->sflags & S_NOREPBRA)
			p_voice->bar_start |= 0x4000;
	}
	bar_type = s->as.u.bar.type;
	if (bar_type == B_COL)			/* ':' */
		return;
	if ((bar_type & 0x0f) != B_COL)		/* if not left repeat bar */
		return;
	if (!(s->sflags & S_RRBAR)) {		/* 'xx:' (not ':xx:') */
		p_voice->bar_start = bar_type & 0x3fff;
		if (s->as.flags & ABC_F_INVIS)
			p_voice->bar_start |= 0x8000;
		if (s->sflags & S_NOREPBRA)
			p_voice->bar_start |= 0x4000;
		if (s->prev && s->prev->type == BAR)
			unlksym(s);
		else
			s->as.u.bar.type = B_BAR;
		return;
	}
	if (bar_type == B_DREP) {		/* '::' */
		s->as.u.bar.type = B_RREP;
		p_voice->bar_start = B_LREP;
		if (s->as.flags & ABC_F_INVIS)
			p_voice->bar_start |= 0x8000;
		if (s->sflags & S_NOREPBRA)
			p_voice->bar_start |= 0x4000;
		return;
	}
	for (i = 0; bar_type != 0; i++)
		bar_type >>= 4;
	bar_type = s->as.u.bar.type;
	s->as.u.bar.type = bar_type >> ((i / 2) * 4);
	i = ((i + 1) / 2 * 4);
	bar_type &= 0x3fff;
	p_voice->bar_start = bar_type & ((1 << i) - 1);
	if (s->as.flags & ABC_F_INVIS)
		p_voice->bar_start |= 0x8000;
	if (s->sflags & S_NOREPBRA)
		p_voice->bar_start |= 0x4000;
}

/* -- move the symbols of an empty staff to the next one -- */
static void sym_staff_move(int staff,
			struct SYMBOL *s,
			struct SYSTEM *sy)
{
	for (;;) {
		if (s->staff == staff
		 && s->type != CLEF) {
			s->staff++;
			s->as.flags |= ABC_F_INVIS;
		}
		s = s->ts_next;
		if (s == tsnext || s->sflags & S_NEW_SY)
			break;
	}
}

/* -- adjust the empty flag in a staff system -- */
static void set_empty(struct SYSTEM *sy)
{
	int staff;

	/* if a system brace has empty and non empty staves, keep all staves */
	for (staff = 0; staff <= nstaff; staff++) {
		int i, empty_fl;

		if (!(sy->staff[staff].flags & (OPEN_BRACE | OPEN_BRACE2)))
			continue;
		empty_fl = 0;
		i = staff;
		while (staff <= nstaff) {
			if (sy->staff[staff].empty)
				empty_fl |= 1;
			else
				empty_fl |= 2;
			if (cursys->staff[staff].flags & (CLOSE_BRACE | CLOSE_BRACE2))
				break;
			staff++;
		}
		if (empty_fl == 3) {	/* if empty and not empty staves */
			while (i <= staff)
				sy->staff[i++].empty = 0;
		}
	}
}

/* -- define the start and end of a piece of tune -- */
/* tsnext becomes the beginning of the next line */
static void set_piece(void)
{
	struct SYSTEM *sy;
	struct SYMBOL *s;
	struct VOICE_S *p_voice;
	struct STAFF_S *p_staff;
	int staff;
	char empty[MAXSTAFF];

	/* reset the staves */
	sy = cursys;
	for (staff = 0; staff <= nstaff; staff++) {
		p_staff = &staff_tb[staff];
		p_staff->y = 0;		/* staff system not computed */
		p_staff->stafflines = sy->staff[staff].stafflines;
		p_staff->staffscale = sy->staff[staff].staffscale;
	}

	/* search the next end of line,
	 * set the repeat measures, (remove some dble bars?)
	 * and flag the empty staves
	 */
	memset(empty, 1, sizeof empty);
	for (s = tsfirst; s; s = s->ts_next) {
		if (s->sflags & S_NL)
			break;
		if (s->sflags & S_NEW_SY) {
			for (staff = 0; staff <= nstaff; staff++) {
				sy->staff[staff].empty = empty[staff];
				empty[staff] = 1;
			}
			set_empty(sy);
			sy = sy->next;
			for (staff = 0; staff <= sy->nstaff; staff++) {
				p_staff = &staff_tb[staff];
				p_staff->stafflines = sy->staff[staff].stafflines;
				if (p_staff->stafflines < 0)
					p_staff->stafflines = 5;
				p_staff->staffscale = sy->staff[staff].staffscale;
				if (p_staff->staffscale == 0)
					p_staff->staffscale = 1;
			}
		}
		if (!empty[s->staff])
			continue;
		switch (s->type) {
		case GRACE:
			empty[s->staff] = 0;
			break;
		case NOTEREST:
		case SPACE:
		case MREST:
			if (cfmt.staffnonote > 1) {
				empty[s->staff] = 0;
			} else if (!(s->as.flags & ABC_F_INVIS)) {
				if (s->as.type == ABC_T_NOTE
				 || cfmt.staffnonote != 0)
					empty[s->staff] = 0;
			}
			break;
		}
	}
	tsnext = s;

	/* set the last empty staves and
	 * define the offsets of the measure bars */
	for (staff = 0; staff <= nstaff; staff++)
		sy->staff[staff].empty = empty[staff];
	set_empty(sy);
	for (staff = 0; staff <= nstaff; staff++) {
		if (sy->staff[staff].empty)
			continue;

		p_staff = &staff_tb[staff];
		p_staff->botbar = p_staff->stafflines <= 3 ? 6 : 0;
		switch (p_staff->stafflines) {
		case 0:
		case 1:
		case 3:	p_staff->topbar = 18; break;
		case 2:	p_staff->topbar = 12; break;
		default:
			p_staff->topbar = 6 * (p_staff->stafflines - 1);
			break;
		}
	}

	/* move the symbols of the empty staves to the next staff */
	sy = cursys;
	for (staff = 0; staff < nstaff; staff++) {
		if (sy->staff[staff].empty)
			sym_staff_move(staff, tsfirst, sy);
	}
	if (sy->next) {
		for (s = tsfirst; s; s = s->ts_next) {
			if (s->sflags & S_NL)
				break;
			if (s->sflags & S_NEW_SY) {
				sy = sy->next;
				for (staff = 0; staff < nstaff; staff++) {
					if (sy->staff[staff].empty)
						sym_staff_move(staff, s, sy);
				}
				if (!sy->next)
					break;
			}
		}
	}

	/* initialize the music line */
	init_music_line();
	if (!empty[cursys->nstaff])
		insert_meter &= ~1;	// no more meter

	/* if last music line, nothing more to do */
	if (!tsnext)
		return;

	s = tsnext;
	s->sflags &= ~S_NL;
	s = s->ts_prev;
	s->ts_next = NULL;

	/* set the end of the voices */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		int voice;

		voice = p_voice - voice_tb;
		for (s = tsnext->ts_prev; s; s = s->ts_prev) {
			if (s->voice == voice) {
				s->next = NULL;
				check_bar(s);
				break;
			}
		}
		if (!s)
			p_voice->sym = NULL;
	}
}

/* -- position the symbols along the staff -- */
static void set_sym_glue(float width)
{
	struct SYMBOL *s;
	float beta0, alfa, beta;
	int some_grace;
	float xmin, x, xmax, spafac;

	/* calculate the whole space of the symbols */
	some_grace = 0;
	s = tsfirst;
	xmin = x = xmax = 0;
	for (;;) {
		if (s->type == GRACE)
			some_grace = 1;
		if (s->sflags & S_SEQST) {
			float space;

			xmin += s->shrink;
			if ((space = s->space) < s->shrink)
				space = s->shrink;
			x += space;
			if (cfmt.stretchstaff)
				space *= 1.8;
			xmax += space;
		}
		if (!s->ts_next)
			break;
		s = s->ts_next;
	}

	/* set max shrink and stretch */
	if (!cfmt.continueall)
		beta0 = BETA_X;
	else
		beta0 = BETA_C;

	/* memorize the glue for the last music line */
	if (tsnext) {
		if (x - width >= 0) {
			beta_last = 0;
		} else {
			beta_last = (width - x) / (xmax - x);	/* stretch */
			if (beta_last > beta0) {
				if (cfmt.stretchstaff) {
					if (!cfmt.continueall
					 && cfmt.linewarn) {
						error(0, s,
						      "Line underfull (%.0fpt of %.0fpt)",
							beta0 * xmax + (1 - beta0) * x,
							width);
					}
				} else {
					width = x;
					beta_last = 0;
				}
			}
		}
	} else {			/* if last music line */
		if (x < width) {
			beta = (width - x) / (xmax - x);	/* stretch */
			if (beta >= beta_last) {
				beta = beta_last * xmax + (1 - beta_last) * x;

				/* shrink underfull last line same as previous */
				if (beta < width * (1. - cfmt.stretchlast))
					width = beta;
			}
		}
	}

	spafac = width / x;			/* space expansion factor */

	/* define the x offsets of all starting symbols */
	x = xmax = 0;
	s = tsfirst;
	for (;;) {
		if (s->sflags & S_SEQST) {
			float new_space;

			new_space = s->shrink;
			if (s->space != 0) {
				if (new_space < s->space * spafac)
					new_space = s->space * spafac;
				xmax += s->space * spafac * 1.8;
			}
			x += new_space;
			xmax += new_space;
			s->x = x;
			s->xmax = xmax;
		}
		if (!s->ts_next)
			break;
		s = s->ts_next;
	}

	/* if the last symbol is not a bar, add some extra space */
	switch (s->type) {
	case BAR:
	case FMTCHG:
		break;
	case CUSTOS:
		x += s->wr;
		xmin += s->wr;
		xmax += s->wr;
		break;
	default: {
		float min;

		min = s->wr;
		while (!(s->sflags & S_SEQST)) {
			s = s->ts_prev;
			if (s->wr > min)
				min = s->wr;
		}
		xmin += min + 3;
		if (tsnext && tsnext->space * 0.8 > s->wr + 4) {
			x += tsnext->space * 0.8 * spafac;
			xmax += tsnext->space * 0.8 * spafac * 1.8;
		} else {
#if 1
			x += min + 4;
			xmax += min + 4;
#else
/*fixme:should calculate the space according to the last symbol duration */
			x += (min + 4) * spafac;
			xmax += (min + 4) * spafac * 1.8;
#endif
		}
		break;
	    }
	}

	/* calculate the exact glue */
	if (x >= width) {
		beta = 0;
		if (x == xmin) {
			alfa = 1;
		} else {
			alfa = (x - width) / (x - xmin);	/* shrink */
			if (alfa > 1) {
				error(0, s,
				      "Line too much shrunk (%.0f/%0.fpt of %.0fpt)",
					xmin, x, width);
// uncomment for staff greater than music line
//				alfa = 1;
			}
		}
		realwidth = xmin * alfa + x * (1 - alfa);
	} else {
		alfa = 0;
		if (xmax > x)
			beta = (width - x) / (xmax - x);	/* stretch */
		else
			beta = 1;				/* (no note) */
		if (beta > beta0) {
			if (!cfmt.stretchstaff)
				beta = 0;
		}
		realwidth = xmax * beta + x * (1 - beta);
	}

	/* set the final x offsets */
	s = tsfirst;
	if (alfa != 0) {
		if (alfa < 1) {
			x = xmin = 0;
			for (; s; s = s->ts_next) {
				if (s->sflags & S_SEQST) {
					xmin += s->shrink * alfa;
					x = xmin + s->x * (1 - alfa);
				}
				s->x = x;
			}
		} else {
			alfa = realwidth / x;
			x = 0;
			for (; s; s = s->ts_next) {
				if (s->sflags & S_SEQST)
					x = s->x * alfa;
				s->x = x;
			}
		}
	} else {
		x = 0;
		for (; s; s = s->ts_next) {
			if (s->sflags & S_SEQST)
				x = s->xmax * beta + s->x * (1 - beta);
			s->x = x;
		}
	}

	/* set the x offsets of the grace notes */
	if (some_grace) {
		for (s = tsfirst; s; s = s->ts_next) {
			struct SYMBOL *g;

			if (s->type != GRACE)
				continue;
			x = s->x - s->wl + (cfmt.gracespace >> 16) * 0.1;
			for (g = s->extra; g; g = g->next)
				if (g->type == NOTEREST)
					g->x += x;
		}
	}
}

/* -- initialize a new music line -- */
static void new_music_line(void)
{
	struct VOICE_S *p_voice;
	struct SYMBOL *s;
	int voice;

	/* set the first symbol of each voice */
	tsfirst->ts_prev = NULL;
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		p_voice->sym = NULL;		/* may have no symbol */
		voice = p_voice - voice_tb;
		for (s = tsfirst; s; s = s->ts_next) {
			if (s->voice == voice) {
				p_voice->sym = s;
				s->prev = NULL;
				break;
			}
		}
	}

}

/* -- initialize the start of generation / new music line -- */
static void gen_init(void)
{
	struct SYMBOL *s;

	for (s = tsfirst ; s; s = s->ts_next) {
		if (s->extra) {
			output_ps(s, 0);
			if (!s->extra && s->type == FMTCHG) {
				unlksym(s);
				if (!tsfirst)
					return;
			}
		}
		if (s->sflags & S_NEW_SY) {
			s->sflags &= ~S_NEW_SY;
			cursys = cursys->next;
		}
		switch (s->type) {
		case CLEF:
		case KEYSIG:
		case TIMESIG:
			continue;
//		default:
//			break;		/* may be Q: */
		}
		return;
	}
	tsfirst = NULL;			/* no more notes */
}

/* -- show the errors -- */
static void error_show(void)
{
	struct SYMBOL *s;

	for (s = tsfirst; s; s = s->ts_next) {
		if (s->as.flags & ABC_F_ERROR) {
			putxy(s->x, staff_tb[s->staff].y + s->y);
			a2b("showerror\n");
		}
	}
}

/* -- delay output until the staves are defined (by draw_systems) -- */
static float delayed_output(float indent)
{
	float line_height;
	char *outbuf_sav, *mbf_sav, tmpbuf[20 * 1024];

	outbuf_sav = outbuf;
	mbf_sav = mbf;
	mbf = outbuf = tmpbuf;
	*outbuf = '\0';
	outft = -1;
	draw_sym_near();
	outbuf = outbuf_sav;
	mbf = mbf_sav;
	outft = -1;
	line_height = draw_systems(indent);
	a2b("%s", tmpbuf);
	return line_height;
}

/* -- generate the music -- */
void output_music(void)
{
	struct VOICE_S *p_voice;
	float lwidth, indent;

	/* set the staff system if any STAVES at start of the next line */
	gen_init();
	if (!tsfirst)
		return;
	check_buffer();
	set_global();			/* initialize the generator */
	if (first_voice->next) {	/* if many voices */
		if (cfmt.combinevoices >= 0)
			combine_voices();
		set_stem_dir();		/* set the stems direction in 'multi' */
	}
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next)
		set_beams(p_voice->sym);	/* decide on beams */
	set_stems();			/* set the stem lengths */
	if (first_voice->next) {	/* when multi-voices */
		set_rest_offset();	/* set the vertical offset of rests */
		set_overlap();		/* shift the notes on voice overlap */
	}
	set_allsymwidth(NULL);		/* set the width of all symbols */

	lwidth = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
		- cfmt.leftmargin - cfmt.rightmargin)
			/ cfmt.scale;
	if (lwidth < 50) {
		error(1, 0, "Bad page width %.1f", lwidth);
		lwidth = 10 CM;
	}
	indent = set_indent();
	cut_tune(lwidth, indent);
	beta_last = 0;
	for (;;) {			/* loop per music line */
		float line_height;

		set_piece();
		indent = set_indent();
		set_sym_glue(lwidth - indent);
		if (indent != 0)
			a2b("%.2f 0 T\n", indent); /* do indentation */
		line_height = delayed_output(indent);
		draw_all_symb();
		draw_all_deco();
		if (showerror)
			error_show();
		bskip(line_height);
		if (indent != 0) {
			a2b("%.2f 0 T\n", -indent);
			insert_meter &= ~2;	// no more indentation
		}
		tsfirst = tsnext;
		gen_init();
		if (!tsfirst)
			break;
		buffer_eob();
		new_music_line();
	}
	outft = -1;
}

/* -- reset the generator -- */
void reset_gen(void)
{
	if (cfmt.fields[0] & (1 << ('M' - 'A')))
		insert_meter = 3;	/* insert meter and indent */
	else
		insert_meter = 2;	/* indent only */
}
abcm2ps-8.5.2/musicfont.fmt0000644000175000017500000000215011662672701013674 0ustar  jefjef% example of musical symbols by font

% --- SVG definitions ---
beginsvg
<style type="text/css">
	@font-face {
	    font-family: 'MusicFont';
	    src: local(FreeSerif);
	    font-weight: normal;
	    font-style: normal;
	}
</style>
endsvg

beginps svg
/tclef{/MusicFont 35 selectfont M -12 0 RM(\ud834\udd1e)show}!
/bclef{/MusicFont 35 selectfont M -12 -3 RM(\ud834\udd22)show}!
/csig{/MusicFont 32 selectfont M -7 0 RM(\ud834\udd34)show}!
% the brace does not work with Opera
/brace{/MusicFont 35 selectfont
	gsave
		T -8 0 M -.04 mul 1 exch scale
		(\ud834\udd14)show
	grestore}!
endps

% --- PostScript definitions ---
beginps nosvg
% with FreeSerif
/tclef{/FreeSerif 35 selectfont M -12 0 RM/g_clef glyphshow}!
/bclef{/FreeSerif 35 selectfont M -12 -3 RM/f_clef glyphshow}!
/csig{/FreeSerif 32 selectfont M -8 0 RM/common_time glyphshow}!
/brace{/FreeSerif 35 selectfont 
	gsave
		T -8 0 M -.04 mul 1 exch scale
		/brace glyphshow
	grestore}!

% with PMW-Music (use "GS_FONTPATH=/usr/local/src/pmw-4.21/psfonts/ gs Out.ps")
%/tclef{/PMW-Music 15 selectfont M -10 0 RM(!)show}!
% I let your search the other symbols..
endps
abcm2ps-8.5.2/newfeatures.abc0000644000175000017500000000615312147345672014166 0ustar  jefjef%%textfont Helvetica-Bold 21
%%center Examples for new features in abc2ps-1.2.5
%%textfont * 16
%%center Oct. 5 1997
%%textfont Times-Roman 16

X:1
T:Scale with Treble Clef
M:C
%%freegchord 1	% abcm2ps: don't print 'b' as a flat sign
K:C treble	% abcm2ps: must be forced because too low at the beginning
L: 1/4
 "C,"C,"D,"D,"E,"E,"F,"F,  "G,"G,"A,"A,"B,"B,\
| "C"C"D"D"E"E"F"F  "G"G"A"A"B"B| "c"c "d"d"e"e"f"f  "g"g"a"a"b"b"c'"c' |

X:2
T:Scale with Bass and Alto Clef
M:C
K:C bass	% abcm2ps: no transposition
L: 1/4
P:Bass
 "C,,,"C,,,"D,,,"D,,,"E,,,"E,,,"F,,,"F,,,  "G,,,"G,,,"A,,,"A,,,"B,,,"B,,,|\
 "C,,"C,,"D,,"D,,"E,,"E,,"F,,"F,,  "G,,"G,,"A,,"A,,"B,,"B,,|\
 "C,"C, "D,"D,"E,"E,"F,"F,  "G,"G,"A,"A,"B,"B,"C"C |
P:Alto
%abcm2ps: don't have a new key signature
%K:C alto
K: clef=alto
 "C,,"C,,"D,,"D,,"E,,"E,,"F,,"F,,  "G,,"G,,"A,,"A,,"B,,"B,,\
| "C,"C,"D,"D,"E,"E,"F,"F,  "G,"G,"A,"A,"B,"B,| "C"C "D"D"E"E"F"F  "G"G"A"A"B"B"c"c |

X:3
T:Clef Changes Within Tune
T:In-Line Info Fields by [..]
% text moved out of tune header - change in abcm2ps-7.x.x
M:C
L: 1/8
K:C
%%begintext align
Here is an example for a block of text which is associated with a specific
tune. It will only be printed if this tune (number 3) is selected.
The text should be placed after the "T:" field and before the
block is terminated by a blank line.
Text which is outside a block is
always printed; for example, the title at the top of the page.
%%endtext
cdef gabc' [K:bass] | C,D,E,F, G,A,B,C [K:D treble] | cdef gabc' |  
%%sep
%%text Note: this line and the separator above are also associated with this tune. 

X:4
T:Vocals
T:Note also the trill
C:Music: Trad.
C:Text: Anonymous
M:C
K:C
L: 1/4
e>e ez || edTc2 | ed(c2 | e2 c2- | Hc4) |]
w: *** 1.~~Three blind mice, three blind mice.___
w: *** 2.~~See how~they run, see how~they ru-uuu-un._

X:6
T:Invisible Rests Using X
M:C
K:C
L: 1/4
"F"z4|"F"x4|"F"z4|"F"x4|"Bb"z4|"Bb"x4|"F"z4|"F"x4|"C"z4|"Bb"x4|"F"z4|"F"x4||

%%leftmargin 3cm

X:5
T:Scotland The Brave
T:Demonstrating the Bagpipe Mode and Output Formatting
%%titleleft
%%titlefont Helvetica-Bold 24
%%subtitlefont Helvetica-Bold 16
%%composerspace 0.4cm
%%composerfont Helvetica 13
%%staffwidth 5.5in
%%scale 0.75
%%staffsep 55
C:Trad.
C:from PS file by Alan S. Watt
P:March
L:1/8
M:4/4
K:HP
e|{g}A2 {GdGe}A>B {gcd}c{e}A {gcd}ce| {ag}a2{g}a2 {GdG}ae {gcd}c{e}A|
   {Gdc}d2 {g}f>d {gcd}ce {gcd}c{e}A|{GdG}B2{gef}e2{A}e>f {g}e/>d/{g}c/>B/|
{g}A2 {GdGe}A>B {gcd}c{e}A {gcd}ce| {ag}a2{g}a2 {GdG}ae {gcd}c{e}A|
   {Gdc}d2 {g}f>d {gcd}ce {gcd}c{e}A|{GdG}B2{g}A>B {G}A2 {gcd}ce||

%%vskip 1cm
%%textfont Times-Roman 16
%%begintext
Summary of changes:

- Bass and alto clefs.
- Vocals.
- In-line info fields can be coded using [...].
- Subtitles now printed without "or:".
- Can be more than one composer field.
- Predefined formats: standard, pretty, pretty2 (flag -p, -P).
- Format page layout by .fmt file selected with flag -F.
- Format page layout by %%-pseudocomments in abc file.
- Other pseudocomments: %%sep, %%vskip, %%newpage.
- Text output using %%text, %%center, and %%begintext .. %%endtext.
- "x" functions like a rest but is invisible on the page.
- Bagpipe mode for K:HP.
%%endtext
abcm2ps-8.5.2/options.txt0000644000175000017500000001410212426223027013401 0ustar  jefjef		abcm2ps command line options
		============================

The list of the command line options may be known running:
	'abcm2ps -h'

The options may be grouped when they have no argument, but the
last one (ex: "-lnGI20").

The options may be disabled when starting with '+' or ending with '-'
(ex: "+MT1" is the same as "-MT1-").

The general output format is the last found in the command line.
It may be:
	"-E" for Encapsulated PostScript, one file per tune
	"-g" for SVG, one file per tune
	"-v" for SVG, one file per page
	"-X" for XHTML+SVG
	"-z" for (X)HTML+SVG with (X)HTML+ABC input
	(none) for PostScript
(see below for more information)

List of the options
-------------------

  -
	Read the abc file from stdin.

  --<format> <value>
	Set the format parameter to <value>. See [1].

  -a <float>
	See: [1] - maxshrink <float>

  -A
	Annotate.
	This option inserts reference elements in the PostScript
	or SVG output.

  -B <int>, +B
	See: [1] - barsperstaff <int>

  -b <int>
	See: [1] - measurefirst <int>

  -c, +c
	See: [1] - continueall <bool>

  -D <dir>
	Search the format files in the directory <dir>.

  -d <unit>
	See: [1] - staffsep <unit>

  -E
	Produce EPS output instead of simple PS.
	In this mode, each tune goes to a different file which
	name is '<name>nnn.eps' or <title>.eps (see option '-O'
	- 'nnn' is a sequence number incremented at each tune
	- output to stdout is forbidden).
	EPS files are normally embedded into Postscript documents,
	but they may be a way to generate graphical images. For
	example, using GhostScript:
		abcm2ps voices -Ee7
		gs -sDEVICE=pngmono -r80 -g590x174 \
			-dBATCH -dNOPAUSE \
			-sOutputFile=quitolis.png Out001.eps
	(the values for -g are the values of the bounding box in
	 the .eps, multiplied by (80 / 72), where 80 is the value
	 for -r, and 72 is the default resolution)

  -e [ <tune index list> ] [ <regular expression> ]
	See: [1] - select [ <tune index list> ] [ <regular expression> ]

  -F <file>, +F
	Read the format (or PostScript) file <file>.
	When omitted, the default type of a format file is '.fmt'.
	In the form '+F', the default format file ('default.fmt') is not
	read.

  -f
	See: [1] - flatbeams <bool>

  -G, +G
	See: [1] - graceslurs <bool>

  -g
	Produce SVG output instead of EPS.
	In this mode each tune goes to a different file which name
	is 'Outnnn.svg' (see option '-O').
	If the output is stdout (option '-O-'), all the SVG images
	are output without XML header.

  -H
	Display the current format values.

  -h
	Quick help, equivalent to "abcm2ps" without any arguments.
	This also shows the default settings for some parameters.

  -I <unit>
	See: [1] - indent <unit>

  -i, +i
	Insert a red cercle around the errors in the PostScript output.

  -j <int>[b], +j
	See: [1] - measurenb <int>

  -k <int>
	Set the size of the PostScript output buffer in Kibytes.
	Setting this value to a higher value permits the
	generation of big tunes with -E or -g.
	The default value is 64.

  -l, +l
	See: [1] - landscape <bool>

  -M, +M
	See: [1] - writefields w <bool>

  -m <unit>
	See: [1] - leftmargin <unit>

  -N <int>, +N
	Number the pages.
	<int> indicates the mode:
		0  no page numbers
		1  at top left
		2  at top right
		3  at top left on even pages, top right on odd pages
		4  at top right on even pages, top left on odd pages
	For compatibility with previous versions, '+N' is the same as
	'-N0', and '-N' is the same as '-N2'.
	If a header is defined ('%%header'), this option is ignored.

  -n, +n
	See: [1] - writehistory <bool>

  -O [ <directory> ] [ <name> ], +O
	Define the output file directory and/or name.
	The directory must end with the directory separator
	('/' for unix/windows, '\' for mac).
	By default, the output file goes to the current directory
	with the name:
		'Out.ps' for PS,
		'Outnnn.eps' for EPS (see option '-E'),
		'Outnnn.svg' for SVG (see options '-g' and '-v') or
		'Out.xhtml' for XHTML+SVG (see options '-X' and '-z').
	'nnn' being a sequence number.
	When <name> is present, it is the name of the file, or it
	replaces 'Out' in the file name.
	If <name> is '=', it is replaced by the name of the ABC
	source file (not for '-z').
	If <name> is '-', the result is output to stdout (not for EPS).
	'+O' resets the output file directory and name to their defaults.

  -q
	Quiet mode.
	When present, only the errors are shown.

  -s <float>
	See: [1] - scale <float>

  -S
	Secure mode.
	When present, file inclusion (%%format and %%EPS) and PostScript
	injection (%%beginps and %%postscript) are disabled.

  -T <int> [ <voice> ], +T [ <int> [<voice> ] ]
	Activate or deactivate tablature drawing.
	<int> is the tablature number as defined in %%tablature.
		There may be only 8 different tablatures.
	<voice> is the voice name, full name or subname as found in V:.
		When absent, apply to all voices.
	Up to 4 such commands may be defined.
	Ex: '-T1flute +T2'
	See: [1] - tablature

  -V
	Show the version number.

  -v
	Produce SVG output instead of simple PS.
	In this mode each page goes to a different file which name
	is 'Outnnn.svg' (see option '-O').

  -w <unit>
	See: [1] - staffwidth <unit>

  -X
	Produce XML + SVG output instead of simple PS.
	The default file name is 'Out.xhtml' (see option '-O').

  -x, +x
	See: [1] - writefields X <bool>

  -z
	Produce SVG images from ABC embedded in markup language files
	(HTML, XHTML..).

	The source file is copied to the output file and the ABC sequences
	are converted to SVG images.
	The ABC sequences start by either
		%abc2...
	or
		X:...
	and stop on the first markup tag ("<..").

	The generation creates one image per block, i.e. a music line
	or a text block. For a same rendering as the other SVG generation
	(-g, -v or -X), don't forget to set the line space to null, for
	example enclosing the ABC sequences by
		<div style="line-height:0"> .. </div>

	There can be only one output file.
	Note that the default output file is 'Out.xhtml', so, don't
	forget to change the file type if you generate HTML (.html)
	or XML (.xml) files.

	See sample8.html for a source example.

  -0, +0
	See: [1] - splittune <bool>

  -1, +1
	See: [1] - oneperpage <bool>

[1] http://moinejf.free.fr/abcm2ps-doc/index.html
abcm2ps-8.5.2/parse.c0000644000175000017500000034313312461124640012434 0ustar  jefjef/*
 * Parsing functions.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2015 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.
 */

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

#include "abc2ps.h"
#include "slre.h"

/* options = external formatting */
struct symsel_s {			/* symbol selection */
	short bar;
	short time;
	char seq;
};
struct brk_s {				/* music line break */
	struct brk_s *next;
	struct symsel_s symsel;
};
struct voice_opt_s {			/* voice options */
	struct voice_opt_s *next;
	struct SYMBOL *s;		/* list of options (%%xxx) */
};
struct tune_opt_s {			/* tune options */
	struct tune_opt_s *next;
	struct voice_opt_s *voice_opts;
	struct SYMBOL *s;		/* list of options (%%xxx) */
};

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 */
int nbar;				/* current measure number */

static struct voice_opt_s *voice_opts, *tune_voice_opts;
static struct tune_opt_s *tune_opts, *cur_tune_opts;
static struct brk_s *brks;
static struct symsel_s clip_start, clip_end;

static INFO info_glob;			/* global info definitions */
static char *deco_glob[256];		/* global decoration table */

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;

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

static void get_clef(struct SYMBOL *s);
static struct abcsym *get_info(struct abcsym *as);
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_tblt(struct VOICE_S *p_voice);
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[NSYMTYPES] = {	/* !! index = symbol type !! */
	0,
	9,	/* 1- note / rest */
	3,	/* 2- space */
	2,	/* 3- bar */
	1,	/* 4- clef */
	5,	/* 5- timesig */
	4,	/* 6- keysig */
	0,	/* 7- tempo */
	0,	/* 8- staves */
	9,	/* 9- mrest */
	0,	/* 10- part */
	3,	/* 11- grace */
	0,	/* 12- fmtchg */
	7,	/* 13- tuplet */
	6,	/* 14- stbrk */
	6	/* 15- custos */
};

/* key signature transposition tables */
static signed char cde2fcg[7] = {0, 2, 4, -1, 1, 3, 5};
static char cgd2cde[7] = {0, 4, 1, 5, 2, 6, 3};

/* -- 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;
	struct decos dc;
	int nb, dt;

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

	/* change the multi-rest (type bar) to a single rest */
	memcpy(&dc, &s->as.u.bar.dc, sizeof dc);
	memset(&s->as.u.note, 0, sizeof s->as.u.note);
	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(s) and rest(s) */
	next = s->next;
	p_voice = &voice_tb[s->voice];
	p_voice->last_sym = s;
	p_voice->time = s->time + dt;
	p_voice->cstaff = s->staff;
	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 = sym_add(p_voice, NOTEREST);
		s2->as.type = ABC_T_REST;
		s2->as.flags = s->as.flags;
		s2->dur = s2->as.u.note.lens[0] = dt;
		s2->head = H_FULL;
		s2->nflags = -2;
		p_voice->time += dt;
	}
	s2->next = next;
	if (next)
		next->prev = s2;

	/* copy the mrest decorations to the last rest */
	memcpy(&s2->as.u.note.dc, &dc, sizeof s2->as.u.note.dc);
}

/* -- sort all symbols by time and vertical sequence -- */
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) {
			set_sy = 0;
			fl = 1;			// start a new sequence
			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->as.u.bar.len == 1)
					mrest_expand(s);
				else if (multi)
					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->as.u.bar.len;
				} else if (nb != s->as.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->as.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) {
		p_voice = &voice_tb[prev->voice];
		p_voice->last_sym = prev;
		s = sym_add(p_voice, FMTCHG);
		s->u = -1;
		s->time = prev->time + prev->dur;
		s->sflags = S_SEQST;
		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;
}

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

	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		if (p_voice->ignore)
			continue;
		for (s = p_voice->sym; s; s = s->next) {
			if (s->time >= staves_found)
				break;
		}
		ns = NULL;
		for ( ; s; s = s->next) {
			switch (s->type) {
#if 0 // test
			case KEYSIG:	/* remove the empty key signatures */
				if (s->as.u.key.empty) {
					if (s->prev)
						s->prev->next = s->next;
					else
						p_voice->sym = s->next;
					if (s->next)
						s->next->prev = s->prev;
					continue;
				}
				break;
#endif
			case FMTCHG:
				s2 = s->extra;
				if (s2) {	/* dummy format */
					if (!ns)
						ns = s2;
					if (s->prev) {
						s->prev->next = s2;
						s2->prev = s->prev;
					}
					if (!s->next) {
						ns = NULL;
						break;
					}
					while (s2->next)
						s2 = s2->next;
					s->next->prev = s2;
					s2->next = s->next;
				}
				/* fall thru */
			case TEMPO:
			case PART:
			case TUPLET:
				if (!ns)
					ns = s;
				continue;
			case MREST:		/* don't shift P: and Q: */
				if (!ns)
					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)
					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;
				s2->next = s->next;
				if (s2->next)
					s2->next->prev = s2;
				else
					p_voice->last_sym = s2;
				s2->prev = s;
				s->next = s2;
				s = s2;
			}
			if (!ns)
				continue;
			s->extra = ns;
			s->prev->next = NULL;
			s->prev = ns->prev;
			if (s->prev)
				s->prev->next = s;
			else
				p_voice->sym = s;
			ns->prev = NULL;
			ns = NULL;
		}

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

/* -- 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; s = s->next) {
//fixme: there may be other symbols before the %%staves at this same time
			if (s->time >= staves_found)
				break;
		}
		for ( ; s; s = s->next) {
			s2 = (struct SYMBOL *) getarena(sizeof *s2);
			memcpy(s2, s, sizeof *s2);
			s2->prev = p_voice2->last_sym;
			s2->next = NULL;
			if (p_voice2->sym)
				p_voice2->last_sym->next = s2;
			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 = NULL;
			g = s2->extra;
			if (!g)
				continue;
			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; 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) {
		memset(new_sy, 0, sizeof *new_sy);
		for (voice = 0; voice < MAXVOICE; voice++) {
			new_sy->voice[voice].range = -1;
		}
		for (staff = 0; staff < MAXSTAFF; staff++) {
			new_sy->staff[staff].stafflines = 5;
			new_sy->staff[staff].staffscale = 1;
		}
		cursys = new_sy;
	} else {
		for (voice = 0; voice < MAXVOICE; voice++) {

			// update the previous system
//			if (parsys->voice[voice].range < 0
//			 || parsys->voice[voice].second)
//				continue;
			staff = parsys->voice[voice].staff;
			if (voice_tb[voice].stafflines >= 0)
				parsys->staff[staff].stafflines =
						voice_tb[voice].stafflines;
			if (voice_tb[voice].staffscale != 0)
				parsys->staff[staff].staffscale =
						voice_tb[voice].staffscale;
		}
		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;
}

/* -- 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 */
	if (!tsfirst)
		return;
//	parsys->nstaff = nstaff;	/* save the number of staves */
}

/* go to a global (measure + time) */
static struct SYMBOL *go_global_time(struct SYMBOL *s,
				struct symsel_s *symsel)
{
	struct SYMBOL *s2;
	int bar_time;

	if (symsel->bar <= 1) {		/* special case: there is no measure 0/1 */
//	 && nbar == -1) {		/* see set_bar_num */
		if (symsel->bar == 0)
			goto chk_time;
		for (s2 = s; s2; s2 = s2->ts_next) {
			if (s2->type == BAR
			 && s2->time != 0)
				break;
		}
		if (s2->time < voice_tb[cursys->top_voice].meter.wmeasure)
			s = s2;
		goto chk_time;
	}
	for ( ; s; s = s->ts_next) {
		if (s->type == BAR
		 && s->u >= symsel->bar)
			break;
	}
	if (!s)
		return NULL;
	if (symsel->seq != 0) {
		int seq;

		seq = symsel->seq;
		for (s = s->ts_next; s; s = s->ts_next) {
			if (s->type == BAR
			 && s->u == symsel->bar) {
				if (--seq == 0)
					break;
			}
		}
		if (!s)
			return NULL;
	}

chk_time:
	if (symsel->time == 0)
		return s;
	bar_time = s->time + symsel->time;
	while (s->time < bar_time) {
		s = s->ts_next;
		if (!s)
			return s;
	}
	do {
		s = s->ts_prev;		/* go back to the previous sequence */
	} while (!(s->sflags & S_SEQST));
	return s;
}

/* treat %%clip */
static void do_clip(void)
{
	struct SYMBOL *s, *s2;
	struct SYSTEM *sy;
	struct VOICE_S *p_voice;
	int voice;

	/* remove the beginning of the tune */
	s = tsfirst;
	if (clip_start.bar > 0
	 || clip_start.time > 0) {
		s = go_global_time(s, &clip_start);
		if (!s) {
			tsfirst = NULL;
			return;
		}

		/* update the start of voices */
		sy = cursys;
		for (s2 = tsfirst; s2 != s; s2 = s2->ts_next) {
			if (s->sflags & S_NEW_SY)
				sy = sy->next;
			switch (s2->type) {
			case CLEF:
				voice_tb[s2->voice].s_clef = s2;
				break;
			case KEYSIG:
				memcpy(&voice_tb[s2->voice].key, &s2->as.u.key,
					sizeof voice_tb[0].key);
				break;
			case TIMESIG:
				memcpy(&voice_tb[s2->voice].meter, &s2->as.u.meter,
					sizeof voice_tb[0].meter);
				break;
			}
		}
		cursys = sy;
		for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
			voice = p_voice - voice_tb;
			for (s2 = s; s2; s2 = s2->ts_next) {
				if (s2->voice == voice) {
					s2->prev = NULL;
					break;
				}
			}
			p_voice->sym = s2;
		}
		tsfirst = s;
		s->ts_prev = NULL;
	}

	/* remove the end of the tune */
	s = go_global_time(s, &clip_end);
	if (!s)
		return;

	/* keep the current sequence */
	do {
		s = s->ts_next;
		if (!s)
			return;
	} while (!(s->sflags & S_SEQST));

	/* cut the voices */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		voice = p_voice - voice_tb;
		for (s2 = s->ts_prev; s2; s2 = s2->ts_prev) {
			if (s2->voice == voice) {
				s2->next = NULL;
				break;
			}
		}
		if (!s2)
			p_voice->sym = NULL;
	}
	s->ts_prev->ts_next = NULL;
}

/* -- set the bar numbers and treat %%clip / %%break -- */
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;
	if (wmeasure == 0)				/* if M:none */
		wmeasure = 1;
	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->u != 0) {
				nbar = s->u;		/* (%%setbarnb) */
				break;
			}
			if (s->as.u.bar.repeat_bar
			 && s->as.text
			 && !cfmt.contbarnb) {
				if (s->as.text[0] == '1') {
					bar_rep = nbar;
				} else {
					nbar = bar_rep; /* restart bar numbering */
					s->u = 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;
			s->prev->next = s;
			s2->prev = s;
			s->ts_next = s2;
			s->ts_prev = s2->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->as.u.meter.wmeasure;
			if (wmeasure == 0)
				wmeasure = 1;
			if (s->time < bar_time)
				bar_time = s->time + wmeasure;
			break;
		case MREST:
			bar_num += s->as.u.bar.len - 1;
			while (s->ts_next
			    && s->ts_next->type != BAR)
				s = s->ts_next;
			break;
		case BAR:
//			if (s->as.flags & ABC_F_INVIS)
//				break;
			if (s->u != 0) {
				bar_num = s->u;		/* (%%setbarnb) */
				if (s->time < bar_time) {
					s->u = 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->as.u.bar.repeat_bar
				 && s2->as.text
				 && !cfmt.contbarnb) {
					if (s2->as.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->u = 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 */
}

/* -- generate a piece of tune -- */
static void generate(void)
{
	int old_lvl, voice;
	struct VOICE_S *p_voice;

	system_init();
	if (!tsfirst)
		return;				/* no symbol */
	set_bar_num();
	if (!tsfirst)
		return;				/* no more symbol */
	old_lvl = lvlarena(2);
	output_music();
	clrarena(2);				/* clear generation */
	lvlarena(old_lvl);

	/* reset the parser */
	for (p_voice = first_voice; p_voice; p_voice = p_voice->next) {
		voice = p_voice - voice_tb;
		p_voice->sym = p_voice->last_sym = NULL;
		p_voice->time = 0;
		p_voice->have_ly = 0;
		p_voice->staff = cursys->voice[voice].staff;
		p_voice->second = cursys->voice[voice].second;
		p_voice->s_clef->time = 0;
	}
	staves_found = 0;		// (for voice compress/dup)
}

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

/* for transpose purpose, check if a pitch is already in the measure
 * in the current voice and return its accidental or (-1) */
static int acc_same_pitch(int pitch)
{
	struct SYMBOL *s;
	int i;

	for (s = curvoice->last_sym; s; s = s->prev) {
		switch (s->as.type) {
		case ABC_T_BAR:
			return -1;	/* no same pitch */
		default:
			continue;
		case ABC_T_NOTE:
			break;
		}

		for (i = 0; i <= s->nhd; i++) {
			if (s->as.u.note.pits[i] == pitch)
				return s->as.u.note.accs[i];
		}
	}
	return -1;
}

/* transpose a note / chord */
static void note_transpose(struct SYMBOL *s)
{
	int i, j, m, n, d, a, dp, i1, i2, i3, i4, sf_old;
	static signed char acc1[6] = {0, 1, 0, -1, 2, -2};
	static char acc2[5] = {A_DF, A_FT, A_NT, A_SH, A_DS};

	m = s->as.u.note.nhd;
	sf_old = curvoice->okey.sf;
	i2 = curvoice->ckey.sf - sf_old;
	dp = cgd2cde[(i2 + 4 * 7) % 7];
	if (curvoice->transpose < 0
	 && dp != 0)
		dp -= 7;
	dp += curvoice->transpose / 3 / 12 * 7;
	for (i = 0; i <= m; i++) {
		n = s->as.u.note.pits[i];
		s->as.u.note.pits[i] += dp;
		i1 = cde2fcg[(n + 5 + 16 * 7) % 7];	/* fcgdaeb */
		a = s->as.u.note.accs[i] & 0x07;
		if (a == 0) {
			if (curvoice->okey.nacc == 0) {
				if (sf_old > 0) {
					if (i1 < sf_old - 1)
						a = A_SH;
				} else if (sf_old < 0) {
					if (i1 >= sf_old + 6)
						a = A_FT;
				}
			} else {
				for (j = 0; j < curvoice->okey.nacc; j++) {
					if ((n + 16 * 7 - curvoice->okey.pits[j]) % 7
								== 0) {
						a = curvoice->okey.accs[j];
						break;
					}
				}
			}
		}
		i3 = i1 + i2 + acc1[a] * 7;

		/* accidental */
		i1 = ((i3 + 1 + 21) / 7 + 2 - 3 + 32 * 5) % 5;
		a = acc2[(unsigned) i1];
		if (curvoice->ckey.empty != 0) {	/* key none */
			int other_acc;

			other_acc = acc_same_pitch(s->as.u.note.pits[i]);
			switch (s->as.u.note.accs[i]) {
			case 0:
				if (other_acc >= 0 || a == A_NT)
					continue;
				break;
			case A_NT:
				break;
			default:
				if (other_acc < 0) {
					if (a == A_NT)
						a = 0;
				} else {
					if (a == other_acc)
						a = 0;
				}
				break;
			}
		} else if (s->as.u.note.accs[i] != 0) {
			;
		} else if (curvoice->ckey.nacc > 0) {	/* acc list */
			i4 = cgd2cde[(unsigned) ((i3 + 16 * 7) % 7)];
			for (j = 0; j < curvoice->ckey.nacc; j++) {
				if ((i4 + 16 * 7 - curvoice->ckey.pits[j]) % 7
							== 0)
					break;
			}
			if (j < curvoice->ckey.nacc)
				continue;
		} else {
			continue;
		}
		i1 = s->as.u.note.accs[i] & 0x07;
		i4 = s->as.u.note.accs[i] >> 3;
		if (i4 != 0				/* microtone */
		 && i1 != a) {				/* different accidental type */
//fixme: strange code !!!
			n = parse.micro_tb[i4];
			d = (n & 0xff) + 1;
			n = (n >> 8) + 1;
			if (a == A_NT) {
/*fixme:should treat the double sharps/flats*/
				if (n >= d) {
					n -= d;
					a = i1;
				} else {
					n = d - n;
					if (i1 == A_SH)
						a = A_FT;
					else
						a = A_SH;
				}
			}
			d--;
			d += (n - 1) << 8;
			for (i4 = 1; i4 < MAXMICRO; i4++) {
				if (parse.micro_tb[i4] == d)
					break;
				if (parse.micro_tb[i4] == 0) {
					parse.micro_tb[i4] = d;
					break;
				}
			}
			if (i4 == MAXMICRO) {
				error(1, s, "Too many microtone accidentals");
				i4 = 0;
			}
		}
		s->as.u.note.accs[i] = (i4 << 3) | a;
	}
}

/* transpose a guitar chord */
static void gch_transpose(struct SYMBOL *s)
{
	char *p, *q, *new_txt;
	int l, latin;
	int n, a, i1, i2, 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", "", "#", "##"};

	p = s->as.text;

	/* skip the annotations */
	for (;;) {
		if (!strchr("^_<>@", *p))
			break;
		while (*p != '\0' && *p != '\n')
			p++;
		if (*p == '\0')
			return;
		p++;
	}

	/* main chord */
	q = p + 1;
	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 = 1;
			n = 0;		/* Do */
			break;
		}
		n = 1;
		break;
	case 'F':
		if (p[1] == 'a')
			latin = 1;	/* Fa */
		n = 3;
		break;
	case 'L':
		latin = 1;		/* La */
		n = 5;
		break;
	case 'M':
		latin = 1;		/* Mi */
		n = 2;
		break;
	case 'R':
		latin = 1;
		if (p[1] != 'e')
			latin++;	/* Ré */
		n = 1;			/* Re */
		break;
	case 'S':
		latin = 1;
		if (p[1] == 'o') {
			latin++;
			n = 4;		/* Sol */
		} else {
			n = 6;		/* Si */
		}
		break;
	default:
		return;
	}
	q += latin;

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

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

	/* bass */
	while (*p != '\0' && *p != '\n' && *p != '/')
		*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;
			i1 = cde2fcg[n];	/* fcgdaeb */
			if (*p == '#') {
				a = 1;
				p++;
			} else if (*p == 'b') {
				a = -1;
				p++;
			} else {
				a = 0;
			}
			i3 = i1 + i2 + a * 7;
			i4 = cgd2cde[(unsigned) ((i3 + 16 * 7) % 7)];
			*new_txt++ = note_names[i4];
			i1 = ((i3 + 1 + 21) / 7 + 2 - 3 + 32 * 5) % 5;
			new_txt += sprintf(new_txt, "%s", acc_name[i1]);
		}
	}
	strcpy(new_txt, p);
}

/* -- build the guitar chords / annotations -- */
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);

	/* 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->as.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) {		/* if not 1st line */
				gch->x = (gch - 1)->x;
				gch->y = (gch - 1)->y - h_ann;
			}
			break;
		}
		gch->idx = p - s->as.text;
		for (;;) {
			switch (*p) {
			default:
				p++;
				continue;
			case '\\':
				p++;
				if (*p == 'n') {
					p[-1] = '\0';
					break;		/* sep = 'n' */
				}
				p++;
				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->as.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->as.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;
		}
	}
}

/* get the note which will receive a lyric word */
static struct SYMBOL *next_lyric_note(struct SYMBOL *s)
{
	while (s
	    && (s->as.type != ABC_T_NOTE
	     || (s->as.flags & ABC_F_GRACE)))
		s = s->next;
	return s;
}

/* -- parse lyric (vocal) lines (w:) -- */
static struct abcsym *get_lyric(struct abcsym *as)
{
	struct SYMBOL *s, *s2;
	char word[128], *p, *q;
	int ln, cont;
	struct FONTSPEC *f;

	curvoice->have_ly = curvoice->posit.voc != SL_HIDDEN;

	if (curvoice->ignore) {
		for (;;) {
			if (!as->next)
				return as;
			switch (as->next->type) {
			case ABC_T_PSCOM:
				as = process_pscomment(as->next);
				continue;
			case ABC_T_INFO:
				if (as->next->text[0] == 'w'
				 || as->next->text[0] == '+') {
					as = as->next;
					continue;
				}
				break;
			}
			return as;
		}
	}

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

	/* treat all w: lines */
	cont = 0;
	ln = -1;
	s2 = s = NULL;				// have gcc happy
	for (;;) {
		if (!cont) {
			if (ln >= MAXLY) {
				error(1, (struct SYMBOL *) as,
					"Too many lyric lines");
				ln--;
			}
			ln++;
			s2 = s;
			s = curvoice->lyric_start;
			if (!s)
				s = curvoice->sym;
			else
				s = s->next;
			if (!s) {
				error(1, (struct SYMBOL *) as, "w: without music");
				return as;
			}
		} else {
			cont = 0;
		}

		/* scan the lyric line */
		p = &as->text[2];
		while (*p != '\0') {
			while (isspace((unsigned char) *p))
				p++;
			if (*p == '\0')
				break;
			if (*p == '\\' && p[1] == '\0') {
				cont = 1;
				break;
			}
			switch (*p) {
			case '|':
				while (s && s->type != BAR) {
					s2 = s;
					s = s->next;
				}
				if (!s) {
					error(1, s2,
						"Not enough bar lines for lyric line");
					goto ly_next;
				}
				s2 = s;
				s = s->next;
				p++;
				continue;
			case '-':
				word[0] = LY_HYPH;
				word[1] = '\0';
				p++;
				break;
			case '_':
				word[0] = LY_UNDER;
				word[1] = '\0';
				p++;
				break;
			case '*':
				word[0] = *p++;
				word[1] = '\0';
				break;
			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 = LY_HYPH;
						goto addch;
					case '\\':
						if (p[1] == '\0')
							break;
						switch (p[1]) {
						case '~':
						case '_':
						case '*':
						case '|':
						case '-':
						case ' ':
						case '\\':
							c = *++p;
							break;
						}
						/* fall thru */
					default:
					addch:
						if (q < &word[sizeof word - 1])
							*q++ = c;
						p++;
						if (c == LY_HYPH)
							break;
						continue;
					}
					break;
				}
				*q = '\0';
				break;
			}

			/* store the word in the next note */
			if (s) {				/* for error */
				s2 = s;
				s = next_lyric_note(s);
			}
			if (!s) {
				if (!s2)
					s2 = (struct SYMBOL *) as;
				error(1, s2, "Too many words in lyric line");
				goto ly_next;
			}
			if (word[0] != '*'
			 && s->posit.voc != SL_HIDDEN) {
				struct lyl *lyl;
				float w;

				if (!s->ly) {
					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);

				/* handle the font changes inside the text */
				while (*q != '\0') {
					if (*q == '$' && isdigit((unsigned char) q[1])
					 && (unsigned) (q[1] - '0') < FONT_UMAX) {
						int ft;

						q++;
						ft = *q - '0';
						if (ft == 0)
							ft = cfmt.vof;
						f = &cfmt.font_tb[ft];
						str_font(ft);
					}
					q++;
				}
			}
			s2 = s;
			s = s->next;
		}

		/* loop if more lyrics */
ly_next:
		for (;;) {
			if (!as->next)
				goto ly_upd;
			switch (as->next->type) {
			case ABC_T_PSCOM:
				as = process_pscomment(as->next);
				f = &cfmt.font_tb[cfmt.vof];	/* may have changed */
				str_font(cfmt.vof);
				continue;
			case ABC_T_INFO:
				if (as->next->text[0] != 'w'
				 && as->next->text[0] != '+')
					goto ly_upd;
				as = as->next;
				if (as->text[0] == '+')
					cont = 1;
				if (!cont) {
					s = next_lyric_note(s);
					if (s) {
						error(1, s,
							"Not enough words for lyric line");
					}
				}
				break;			/* more lyric */
			default:
				goto ly_upd;
			}
			break;
		}
	}

	/* the next lyrics will go into the next notes */
ly_upd:
//fixme: no error with abc-2.1
	if (next_lyric_note(s))
		error(0, s, "Not enough words for lyric line");
	// fill the w: with 'blank syllabes'
	curvoice->lyric_start = curvoice->last_sym;
	return as;
}

/* -- 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)
			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)  {
		p_voice->last_sym->sflags |= S_BEAM_END;
		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 */
	p_voice->last_sym->sflags |= S_BEAM_END;
	voice2 = s->as.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;
		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_voice < 0) {
			over_mxtime = p_voice->time;
			over_voice = voice;
		} else if (p_voice->time != over_mxtime)
			error(1, s, tx_wrong_dur);
	}
	p_voice2->time = over_time;
	curvoice = p_voice2;
}

struct staff_s {
	short voice;
	short flags;
};

/* -- parse %%staves / %%score -- */
static void parse_staves(struct SYMBOL *s,
			struct staff_s *staves)
{
	char *p;
	int voice, flags_st, brace, bracket, parenth, err;
	short flags;
	struct staff_s *p_staff;

	/* define the voices */
	err = 0;
	flags = 0;
	brace = bracket = parenth = 0;
	flags_st = 0;
	voice = 0;
	p = s->as.text + 7;
	while (*p != '\0' && !isspace((unsigned char) *p))
		p++;
	while (*p != '\0') {
		switch (*p) {
		case ' ':
		case '\t':
			break;
		case '[':
			if (parenth || brace + bracket >= 2) {
				error(1, s, "Misplaced '[' in %%%%staves");
				err = 1;
				break;
			}
			if (brace + bracket == 0)
				flags |= OPEN_BRACKET;
			else
				flags |= OPEN_BRACKET2;
			bracket++;
			flags_st <<= 8;
			flags_st |= OPEN_BRACKET;
			break;
		case '{':
			if (parenth || brace || bracket >= 2) {
				error(1, s, "Misplaced '{' in %%%%staves");
				err = 1;
				break;
			}
			if (bracket == 0)
				flags |= OPEN_BRACE;
			else
				flags |= OPEN_BRACE2;
			brace++;
			flags_st <<= 8;
			flags_st |= OPEN_BRACE;
			break;
		case '(':
			if (parenth) {
				error(1, s, "Misplaced '(' in %%%%staves");
				err = 1;
				break;
			}
			flags |= OPEN_PARENTH;
			parenth++;
			flags_st <<= 8;
			flags_st |= OPEN_PARENTH;
			break;
		case '*':
			if (brace && !parenth && !(flags & (OPEN_BRACE | OPEN_BRACE2)))
				flags |= FL_VOICE;
			break;
		case '+':
			flags |= MASTER_VOICE;
			break;
		default:
			if (!isalnum((unsigned char) *p) && *p != '_') {
				error(1, s, "Bad voice ID in %%%%staves");
				err = 1;
				break;
			}
			if (voice >= MAXVOICE) {
				error(1, s, "Too many voices in %%%%staves");
				err = 1;
				break;
			}
			{
				int i, v;
				char sep, *q;

				q = p;
				while (isalnum((unsigned char) *p) || *p == '_')
					p++;
				sep = *p;
				*p = '\0';

				/* search the voice in the voice table */
				v = -1;
				for (i = 0; i < MAXVOICE; i++) {
					if (strcmp(q, voice_tb[i].id) == 0) {
						v = i;
						break;
					}
				}
				if (v < 0) {
					error(1, s,
						"Voice '%s' of %%%%staves has no symbol",
						q);
					err = 1;
//					break;
					p_staff = staves;
				} else {
					p_staff = staves + voice++;
					p_staff->voice = v;
				}
				*p = sep;
			}
			for ( ; *p != '\0'; p++) {
				switch (*p) {
				case ' ':
				case '\t':
					continue;
				case ']':
					if (!(flags_st & OPEN_BRACKET)) {
						error(1, s,
							"Misplaced ']' in %%%%staves");
						err = 1;
						break;
					}
					bracket--;
					if (brace + bracket == 0)
						flags |= CLOSE_BRACKET;
					else
						flags |= CLOSE_BRACKET2;
					flags_st >>= 8;
					continue;
				case '}':
					if (!(flags_st & OPEN_BRACE)) {
						error(1, s,
							"Misplaced '}' in %%%%staves");
						err = 1;
						break;
					}
					brace--;
					if (bracket == 0)
						flags |= CLOSE_BRACE;
					else
						flags |= CLOSE_BRACE2;
					flags &= ~FL_VOICE;
					flags_st >>= 8;
					continue;
				case ')':
					if (!(flags_st & OPEN_PARENTH)) {
						error(1, s,
							"Misplaced ')' in %%%%staves");
						err = 1;
						break;
					}
					parenth--;
					flags |= CLOSE_PARENTH;
					flags_st >>= 8;
					continue;
				case '|':
					flags |= STOP_BAR;
					continue;
				}
				break;
			}
			p_staff->flags = flags;
			flags = 0;
			if (*p == '\0')
				break;
			continue;
		}
		if (*p == '\0')
			break;
		p++;
	}
	if (flags_st != 0) {
		error(1, s, "'}', ')' or ']' missing in %%%%staves");
		err = 1;
	}
	if (err) {
		int i;

		for (i = 0; i < voice; i++)
			staves[i].flags = 0;
	}
	if (voice < MAXVOICE)
		staves[voice].voice = -1;
}

/* -- get staves definition (%%staves / %%score) -- */
static void get_staves(struct SYMBOL *s)
{
	struct VOICE_S *p_voice, *p_voice2;
	struct staff_s *p_staff, staves[MAXVOICE];
	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 != 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[p_voice - 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;
		sym_link(s, STAVES);	/* link the staves in the current voice */
		s->as.state = ABC_S_HEAD; /* (output PS sequences immediately) */
		parsys->nstaff = nstaff;
		system_new();
	}
	staves_found = maxtime;

	memset(staves, 0, sizeof staves);
	parse_staves(s, staves);

	/* 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->as.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->as.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];
}

/* -- 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 = NULL;
		p_voice->lyric_start = NULL;
		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;
	}
}

/* output a pdf mark */
static void put_pdfmark(char *p)
{
	unsigned char c, *q;
	int u;

	p = trim_title(p, NULL);

	/* check if pure ASCII without '\', '(' nor ')'*/
	for (q = (unsigned char *) p; *q != '\0'; q++) {
		switch (*q) {
		case '\\':
		case '(':
		case ')':
			break;
		default:
			if (*q >= 0x80)
				break;
			continue;
		}
		break;
	}
	if (*q == '\0') {
		a2b("[/Title(%s)/OUT pdfmark\n", p);
		return;
	}

	/* build utf-8 mark */
	a2b("[/Title<FEFF");
	q = (unsigned char *) p;
	u = -1;
	while (*q != '\0') {
		c = *q++;
		if (c < 0x80) {
			if (u >= 0) {
				a2b("%04X", u);
				u = -1;
			}
			a2b("%04X", (int) c);
			continue;
		}
		if (c < 0xc0) {
			u = (u << 6) | (c & 0x3f);
			continue;
		}
		if (u >= 0) {
			a2b("%04X", u);
			u = -1;
		}
		if (c < 0xe0)
			u = c & 0x1f;
		else if (c < 0xf0)
			u = c & 0x0f;
		else
			u = c & 0x07;
	}
	if (u >= 0) {
		a2b("%04X", u);
		u = -1;
	}
	a2b(">/OUT pdfmark\n");
}

/* rebuild a tune header for %%tune filter */
static char *tune_header_rebuild(struct abcsym *as)
{
	struct abcsym *as2;
	char *header, *p;
	int len;

	len = 0;
	as2 = as;
	for (;;) {
		if (as2->type == ABC_T_INFO) {
			len += strlen(as2->text) + 1;
			if (as2->text[0] == 'K')
				break;
		}
		as2 = as2->next;
	}
	header = malloc(len + 1);
	p = header;
	for (;;) {
		if (as->type == ABC_T_INFO) {
			strcpy(p, as->text);
			p += strlen(p);
			*p++ = '\n';
			if (as->text[0] == 'K')
				break;
		}
		as = as->next;
	}
	*p++ = '\0';
	return header;
}

/* apply the options to the current tune */
static void tune_filter(struct abcsym *as)
{
	struct tune_opt_s *opt;
	struct SYMBOL *s, *s2;
	struct slre slre;
	char *header, *p;

	header = tune_header_rebuild(as);
	for (opt = tune_opts; opt; opt = opt->next) {
		struct abcsym *last_staves;

		p = &opt->s->as.text[2 + 5];	/* "%%tune RE" */
		while (isspace((unsigned char) *p))
			p++;
		if (!slre_compile(&slre, p))
			continue;
		if (!slre_match(&slre, header, strlen(header), 0))
			continue;

		/* apply the options */
		cur_tune_opts = opt;
		last_staves = as->next;
		for (s = opt->s->next; s; s = s->next) {
			struct abcsym *as2;

			/* replace the next %%staves/%%score */
			as2 = (struct abcsym *) s;
			if (as2->type == ABC_T_PSCOM
			 && (strncmp(&as2->text[2], "staves", 6) == 0
			  || strncmp(&as2->text[2], "score", 5) == 0)) {
				while (last_staves) {
					if (last_staves->type == ABC_T_PSCOM
					 && (strncmp(&last_staves->text[2],
								"staves", 6) == 0
					  || strncmp(&last_staves->text[2],
								 "score", 5) == 0)) {
						last_staves->text = as2->text;
						last_staves = last_staves->next;
						break;
					}
					last_staves = last_staves->next;
				}
				continue;
			}
			s2 = (struct SYMBOL *) getarena(sizeof *s2);
			memcpy(s2, s, sizeof *s2);
			process_pscomment((struct abcsym *) s2);
		}
		cur_tune_opts = NULL;
		tune_voice_opts = opt->voice_opts;	// for %%voice
//fixme: what if many %%tune's with %%voice inside?
	}
	free(header);
}

/* apply the options of the current voice */
static void voice_filter(void)
{
	struct voice_opt_s *opt;
	struct SYMBOL *s;
	struct slre slre;
	int pass;
	char *p;

	/* scan the global, then the tune options */
	pass = 0;
	opt = voice_opts;
	for (;;) {
		if (!opt) {
			if (pass != 0)
				break;
			opt = tune_voice_opts;
			if (!opt)
				break;
			pass++;
		}
		p = &opt->s->as.text[2 + 6];	/* "%%voice RE" */
		while (isspace((unsigned char) *p))
			p++;
		if (!slre_compile(&slre, p))
			goto next_voice;
		if (!slre_match(&slre, curvoice->id, strlen(curvoice->id), 0)
		 && (!curvoice->nm
		  || !slre_match(&slre, curvoice->nm, strlen(curvoice->nm), 0)))
			goto next_voice;

		/* apply the options */
		for (s = opt->s->next; s; s = s->next) {
			struct SYMBOL *s2;

			s2 = (struct SYMBOL *) getarena(sizeof *s2);
			memcpy(s2, s, sizeof *s2);
			process_pscomment((struct abcsym *) s2);
		}
next_voice:
		opt = opt->next;
	}
}

/* -- check if a pseudo-comment may be in the tune header -- */
static int check_header(struct abcsym *as)
{
	switch (as->text[2]) {
	case 'E':
		if (strncmp(as->text + 3, "PS", 2) == 0)
			return 0;
		break;
	case 'm':
		if (strncmp(as->text + 3, "ulticol", 7) == 0)
			return 0;
		break;
	}
	return 1;
}

/* -- set the global definitions after the first K: or middle-tune T:'s -- */
static void set_global_def(void)
{
	struct VOICE_S *p_voice;
	int i;

	for (i = MAXVOICE, p_voice = voice_tb;
	     --i >= 0;
	     p_voice++) {
		if (p_voice->key.mode >= BAGPIPE
		 && p_voice->posit.std == 0)
			p_voice->posit.std = SL_BELOW;
		p_voice->transpose = cfmt.transpose;
//		if (p_voice->key.empty)
//			p_voice->key.sf = 0;
		if (!cfmt.autoclef
		 && p_voice->s_clef
		 && (p_voice->s_clef->sflags & S_CLEF_AUTO)) {
			p_voice->s_clef->as.u.clef.type = TREBLE;
			p_voice->s_clef->sflags &= ~S_CLEF_AUTO;
		}
	}

	/* switch to the 1st voice */
	curvoice = &voice_tb[parsys->top_voice];
}

/* -- get the global definitions after the first K: or middle-tune T:'s -- */
static struct abcsym *get_global_def(struct abcsym *as)
{
	struct abcsym *as2;

	for (;;) {
		as2 = as->next;
		if (!as2)
			break;
		switch (as2->type) {
		case ABC_T_INFO:
			switch (as2->text[0]) {
			case 'K':
				as = as2;
				as->state = ABC_S_HEAD;
				get_key((struct SYMBOL *) as);
				continue;
			case 'I':
			case 'M':
			case 'Q':
				as = as2;
				as->state = ABC_S_HEAD;
				as = get_info(as);
				continue;
			}
			break;
		case ABC_T_PSCOM:
			if (!check_header(as2))
				break;
			as = as2;
			as->state = ABC_S_HEAD;
			as = process_pscomment(as);
			continue;
		}
		break;
	}
	set_global_def();
	return as;
}

/* -- identify info line, store in proper place	-- */
static struct abcsym *get_info(struct abcsym *as)
{
	struct SYMBOL *s, *s2;
	struct VOICE_S *p_voice;
	char *p;
	char info_type;
	int old_lvl;
	static char *state_txt[] = {"global", "header", "tune"};

	/* change arena to global or tune */
	old_lvl = lvlarena(as->state != ABC_S_GLOBAL);

	s = (struct SYMBOL *) as;
	info_type = as->text[0];
	switch (info_type) {
	case 'd':
		break;
	case 'I':
		as = process_pscomment(as);	/* same as pseudo-comment */
		break;
	case 'K':
		get_key(s);
		if (as->state != ABC_S_HEAD)
			break;
		info['K' - 'A'] = s;		/* first K:, end of tune header */
		tunenum++;

		if (!epsf) {
			if (!cfmt.oneperpage)
				use_buffer = !cfmt.splittune;
			bskip(cfmt.topspace);
		}
		write_heading();
		block_put();

		/* information for index
		 * (pdfmark must be after title show for Adobe Distiller) */
		s2 = info['T' - 'A'];
		p = &s2->as.text[2];
		if (*p != '\0') {
			a2b("%% --- font ");
			outft = -1;
			set_font(TITLEFONT);		/* font in comment */
			a2b("\n");
			outft = -1;
		}
		if (cfmt.pdfmark) {
			if (*p != '\0')
				put_pdfmark(p);
			if (cfmt.pdfmark > 1) {
				for (s2 = s2->next; s2; s2 = s2->next) {
					p = &s2->as.text[2];
					if (*p != '\0')
						put_pdfmark(p);
				}
			}
		}

		nbar = cfmt.measurefirst;	/* measure numbering */
		over_voice = -1;
		over_time = -1;
		over_bar = 0;
		reset_gen();

		as = get_global_def(as);

		if (!(cfmt.fields[0] & (1 << ('Q' - 'A'))))
			info['Q' - 'A'] = NULL;

		/* apply the filter for the voice '1' */
		voice_filter();

		/* activate the default tablature if not yet done */
		if (!first_voice->tblts[0])
			set_tblt(first_voice);
		break;
	case 'L':
		switch (as->state) {
		case ABC_S_HEAD: {
			int i, auto_len;

			auto_len = as->u.length.base_length < 0;

			for (i = MAXVOICE, p_voice = voice_tb;
			     --i >= 0;
			     p_voice++)
				p_voice->auto_len = auto_len;
			break;
		    }
		case ABC_S_TUNE:
			curvoice->auto_len = as->u.length.base_length < 0;
			break;
		}
		break;
	case 'M':
		get_meter(s);
		break;
	case 'P': {
		struct VOICE_S *curvoice_sav;

		if (as->state != ABC_S_TUNE) {
			info['P' - 'A'] = s;
			break;
		}

		if (!(cfmt.fields[0] & (1 << ('P' - 'A'))))
			break;
		p_voice = &voice_tb[parsys->top_voice];

		/* if not main voice and if voices are synchronized
		 * then, misplaced P: goes in the main voice */ 
		if (curvoice != p_voice
		 && curvoice->time == p_voice->time) {
			curvoice_sav = curvoice;
			curvoice = p_voice;
			sym_link(s, PART);
			curvoice = curvoice_sav;
			break;
		}
		sym_link(s, PART);
		break;
	    }
	case 'Q':
		if (!(cfmt.fields[0] & (1 << ('Q' - 'A'))))
			break;
		if (as->state != ABC_S_TUNE) {
			info['Q' - 'A'] = s;
			break;
		}
		if (curvoice != &voice_tb[parsys->top_voice])
			break;		/* tempo only for first voice */
		s2 = curvoice->last_sym;
		if (s2) {			/* keep last Q: */
			int tim;

			tim = s2->time;
			do {
				if (s2->type == TEMPO) {
					if (!s2->next)
						curvoice->last_sym = s2->prev;
					else
						s2->next->prev = s2->prev;
					if (!s2->prev)
						curvoice->sym = s2->next;
					else
						s2->prev->next = s2->next;
					break;
				}
				s2 = s2->prev;
			} while (s2 && s2->time == tim);
		}
		sym_link(s, TEMPO);
		break;
	case 'r':
	case 's':
		break;
	case 'T':
		if (as->state == ABC_S_GLOBAL)
			break;
		if (as->state == ABC_S_HEAD)		/* in tune header */
			goto addinfo;
		gen_ly(1);				/* in tune */
		p = &as->text[2];
		if (*p != '\0') {
			write_title(s);
			a2b("%% --- + (%s) ---\n", p);
			if (cfmt.pdfmark)
				put_pdfmark(p);
		}
		voice_init();
		reset_gen();		/* (display the time signature) */
		as = get_global_def(as);
		break;
	case 'U':
		deco[as->u.user.symbol] = parse.deco_tb[as->u.user.value - 128];
		break;
	case 'u':
		break;
	case 'V':
		get_voice(s);

		/* handle here the possible clef which could be replaced
		 * in case of filter */
		if (as->next && as->next->type == ABC_T_CLEF) {
			as = as->next;
			get_clef((struct SYMBOL *) as);
		}
		if (as->state == ABC_S_TUNE
		 && !curvoice->last_sym
		 && curvoice->time == 0)
			voice_filter();
		break;
	case 'w':
		if (as->state != ABC_S_TUNE)
			break;
		if (!(cfmt.fields[1] & (1 << ('w' - 'a')))) {
			while (as->next) {
				if (as->next->type != ABC_T_INFO
				 || as->next->text[0] != '+')
					break;
				as = as->next;
			}
			break;
		}
		as = get_lyric(as);
		break;
	case 'W':
		if (as->state == ABC_S_GLOBAL
		 || !(cfmt.fields[0] & (1 << ('W' - 'A'))))
			break;
		goto addinfo;
	case 'X':
		if (!epsf) {
			buffer_eob();	/* flush stuff left from %% lines */
			write_buffer();
		}
		memcpy(&dfmt, &cfmt, sizeof dfmt); /* save global values */
		memcpy(&info_glob, &info, sizeof info_glob);
		memcpy(deco_glob, deco, sizeof deco_glob);
		info['X' - 'A'] = s;
		if (tune_opts)
			tune_filter(as);
		break;
	default:
		if (info_type >= 'A' && info_type <= 'Z') {
			struct SYMBOL *prev;

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

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

/* -- adjust the duration and time of symbols in a measure when L:auto -- */
static void adjust_dur(struct SYMBOL *s)
{
	struct SYMBOL *s2;
	int time, auto_time;

	/* search the start of the measure */
	s2 = curvoice->last_sym;
	if (!s2)
		return;

	/* the bar time is correct if there is multi-rests */
	if (s2->type == MREST
	 || s2->type == BAR)		/* in second voice */
		return;
	while (s2->type != BAR && s2->prev)
		s2 = s2->prev;
	time = s2->time;
	auto_time = curvoice->time - time;

	/* remove the invisible rest at start of tune */
	if (time == 0) {
		while (s2 && s2->dur == 0)
			s2 = s2->next;
		if (s2 && s2->as.type == ABC_T_REST
		 && (s2->as.flags & ABC_F_INVIS)) {
			time += s2->dur * curvoice->wmeasure / auto_time;
			if (s2->prev)
				s2->prev->next = s2->next;
			else
				curvoice->sym = s2->next;
			if (s2->next)
				s2->next->prev = s2->prev;
			s2 = s2->next;
		}
	}
	if (curvoice->wmeasure == auto_time)
		return;				/* already good duration */

	for (; s2; s2 = s2->next) {
		int i, head, dots, nflags;

		s2->time = time;
		if (s2->dur == 0
		 || (s2->as.flags & ABC_F_GRACE))
			continue;
		s2->dur = s2->dur * curvoice->wmeasure / auto_time;
		time += s2->dur;
		if (s2->type != NOTEREST)
			continue;
		for (i = 0; i <= s2->nhd; i++)
			s2->as.u.note.lens[i] = s2->as.u.note.lens[i]
					 * curvoice->wmeasure / auto_time;
		identify_note(s2, s2->as.u.note.lens[0],
				&head, &dots, &nflags);
		s2->head = head;
		s2->dots = dots;
		s2->nflags = nflags;
		if (s2->nflags <= -2)
			s2->as.flags |= ABC_F_STEMLESS;
		else
			s2->as.flags &= ~ABC_F_STEMLESS;
	}
	curvoice->time = s->time = time;
}

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

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

	bar_type = s->as.u.bar.type;
	s2 = curvoice->last_sym;
	if (s2 && s2->type == BAR) {

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

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

	/* link the bar in the voice */
	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;
	case (B_COL << 8) + (B_BAR << 4) + B_COL:
	case (B_COL << 12) + (B_BAR << 8) + (B_BAR << 4) + B_COL:
		bar_type = (B_COL << 4) + B_COL;	/* :|: and :||: -> :: */
		s->as.u.bar.type = bar_type;
		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 (s->as.u.bar.dc.n > 0)
		deco_cnv(&s->as.u.bar.dc, s, 0); /* convert the decorations */

	/* build the gch */
gch_build:
	if (s->as.text) {
		if (!s->as.u.bar.repeat_bar) {
			gch_build(s);	/* build the guitar chords */
		} else {
			s->gch = getarena(sizeof *s->gch * 2);
			memset(s->gch, 0, sizeof *s->gch * 2);
			s->gch->type = 'r';
			s->gch->font = REPEATFONT;
			str_font(REPEATFONT);
			s->gch->w = tex_str(s->as.text);
			s->gch->x = 4 + 4;
		}
	}
}

/* -- 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->id) != 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(void)
{
	struct VOICE_S *p_voice;
	struct abcsym *as;
	struct SYMBOL *s, *s2;
	int i;

	/* initialize */
	lvlarena(0);
	nstaff = 0;
	staves_found = -1;
	for (i = 0; i < MAXVOICE; i++) {
		p_voice = &voice_tb[i];
		s = (struct SYMBOL *) getarena(sizeof *s);
		memset(s, 0, sizeof *s);
		s->type = CLEF;
		s->voice = i;
		if (cfmt.autoclef) {
			s->as.u.clef.type = AUTOCLEF;
			s->sflags = S_CLEF_AUTO;
		} else {
			s->as.u.clef.type = TREBLE;
		}
		s->as.u.clef.line = 2;		/* treble clef on 2nd line */
		p_voice->s_clef = s;
		p_voice->meter.nmeter = 1;
		p_voice->meter.wmeasure = BASE_LEN;
		p_voice->meter.meter[0].top[0] = '4';
		p_voice->meter.meter[0].bot[0] = '4';
		p_voice->wmeasure = BASE_LEN;
		p_voice->scale = 1;
		p_voice->clone = -1;
		p_voice->over = -1;
		p_voice->posit = cfmt.posit;
		p_voice->stafflines = -1;
//		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) {
		if (cfmt.oneperpage) {
			use_buffer = 0;
			close_page();
		} else {
			use_buffer = !cfmt.splittune;

		}
	} else {
		use_buffer = 1;
		marg_init();
	}

	/* set the duration of all notes/rests
	 *	(this is needed for tuplets and the feathered beams) */
	for (as = parse.first_sym; as; 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;
		}
	}

	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 (as = parse.first_sym; as; as = as->next) {
		s = (struct SYMBOL *) as;
		if (as->flags & ABC_F_LYRIC_START)
			curvoice->lyric_start = curvoice->last_sym;
		switch (as->type) {
		case ABC_T_INFO:
			as = get_info(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
			 || (as->flags & ABC_F_SPACE))
				curvoice->space = 1;
			if (cfmt.continueall || cfmt.barsperstaff
			 || as->u.eoln.type == 1)	/* if '\' */
				continue;
			if (as->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;
			while (as->next) {		/* treat the lyrics */
				if (as->next->type != ABC_T_INFO)
					break;
				switch (as->next->text[0]) {
				case 'w':
					as = get_info(as->next);
					s = (struct SYMBOL *) as;
					continue;
				case 'd':
				case 's':
					as = as->next;
					s = (struct SYMBOL *) as;
					continue;
				}
				break;
			}
			i = (curvoice - voice_tb) + 1;
			if (i < cfmt.alignbars) {
				curvoice = &voice_tb[i];
				continue;
			}
			generate();
			buffer_eob();
			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)
				gch_build(s);	/* build the 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 || 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.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.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.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();
	if (epsf)
		write_eps();
	else
		write_buffer();

	if (info['X' - 'A']) {
		memcpy(&cfmt, &dfmt, sizeof cfmt); /* restore global values */
		memcpy(&info, &info_glob, sizeof info);
		memcpy(deco, deco_glob, sizeof deco);
		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) */
	}
}

/* check if a K: or M: may go to the tune key and time signatures */
static int is_tune_sig(void)
{
	struct SYMBOL *s;

	if (!curvoice->sym)
		return 1;
	if (curvoice->time != 0)
		return 0;		/* not at start of tune */
	for (s = curvoice->sym; s; s = s->next) {
		switch (s->type) {
		case TEMPO:
		case PART:
		case FMTCHG:
			break;
		default:
			return 0;
		}
	}
	return 1;
}

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

	p_voice = curvoice;
	s->type = CLEF;
	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;
			for (voice = 0; voice < MAXVOICE; voice++) {
				voice_tb[voice].s_clef = s;
				if (s->as.u.clef.type == PERC)
					voice_tb[voice].perc = 1;
			}
			return;
		case 'V':	/* clef relative to a voice definition in the header */
			p_voice = &voice_tb[(int) s->as.prev->u.voice.voice];
			curvoice = p_voice;
			break;
		}
	}

	if (is_tune_sig()) {
		p_voice->s_clef = s;
	} else {				/* clef change */

#if 1
		sym_link(s, CLEF);
#else
		/* the clef must appear before a key signature or a bar */
		s2 = p_voice->last_sym;
		if (s2 && s2->prev
		 && (s2->type == KEYSIG || s2->type == BAR)) {
			struct SYMBOL *s3;

			for (s3 = s2; s3->prev; s3 = s3->prev) {
				switch (s3->prev->type) {
				case KEYSIG:
				case BAR:
					continue;
				}
				break;
			}
			p_voice->last_sym = s3->prev;
			sym_link(s, CLEF);
			s->next = s3;
			s3->prev = s;
			p_voice->last_sym = s2;
		} else {
			sym_link(s, CLEF);
		}
#endif
		s->u = 1;			/* small clef */
	}
	p_voice->perc = s->as.u.clef.type == PERC;
	if (s->as.u.clef.type == AUTOCLEF)
		s->sflags |= S_CLEF_AUTO;
}

/* -- treat %%clef -- */
static void clef_def(struct SYMBOL *s)
{
	char *p;
	int clef, clef_line;
	char str[80];

	clef = -1;
	clef_line = 2;
	p = &s->as.text[2 + 5];		/* skip %%clef */
	while (isspace((unsigned char) *p))
		p++;

	/* clef name */
	switch (*p) {
	case '\"':			/* user clef name */
		p = get_str(str, p, sizeof str);
		s->as.u.clef.name = (char *) getarena(strlen(str) + 1);
		strcpy(s->as.u.clef.name, str);
		clef = TREBLE;
		break;
	case 'G':
		clef = TREBLE;
		p++;
		break;
	case 'F':
		clef = BASS;
		clef_line = 4;
		p++;
		break;
	case 'C':
		clef = ALTO;
		clef_line = 3;
		p++;
		break;
	case 'P':
		clef = PERC;
		p++;
		break;
	case 't':
		if (strncmp(p, "treble", 6) == 0) {
			clef = TREBLE;
			p += 6;
		}
		if (strncmp(p, "tenor", 5) == 0) {
			clef = ALTO;
			clef_line = 4;
			p += 5;
		}
		break;
	case 'a':
		if (strncmp(p, "alto", 4) == 0) {
			clef = ALTO;
			clef_line = 3;
			p += 4;
		} else if (strncmp(p, "auto", 4) == 0) {
			clef = AUTOCLEF;
			s->sflags |= S_CLEF_AUTO;
			p += 4;
		}
		break;
	case 'b':
		if (strncmp(p, "bass", 4) == 0) {
			clef = BASS;
			clef_line = 4;
			p += 4;
		}
		break;
	case 'p':
		if (strncmp(p, "perc", 4) == 0) {
			clef = PERC;
			p += 4;
		}
		break;
	case 'n':
		if (strncmp(p, "none", 4) == 0) {
			clef = TREBLE;
			s->as.u.clef.invis = 1;
			s->as.flags |= ABC_F_INVIS;
			p += 4;
		}
		break;
	}
	if (clef < 0) {
		error(1, s, "Unknown clef '%s'", p);
		return;
	}

	/* clef line */
	switch (*p) {
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
		clef_line = *p++ - '0';
		break;
	}

	/* +/-/^/_8 */
	if (p[1] == '8') {
		switch (*p) {
		case '^':
			s->as.u.clef.transpose = -7;
		case '+':
			s->as.u.clef.octave = 1;
			break;
		case '_':
			s->as.u.clef.transpose = 7;
		case '-':
			s->as.u.clef.octave = -1;
			break;
		}
	}

	/* handle the clef */
	s->as.type = ABC_T_CLEF;
	s->as.u.clef.type = clef;
	s->as.u.clef.line = clef_line;
	get_clef(s);
}

/* transpose a key */
static void key_transpose(struct key_s *key)
{
	int t, sf;

	t = curvoice->transpose / 3;
	sf = (t & ~1) + (t & 1) * 7 + key->sf;
	switch ((curvoice->transpose + 210) % 3) {
	case 1:
		sf = (sf + 4 + 12 * 4) % 12 - 4;	/* more sharps */
		break;
	case 2:
		sf = (sf + 7 + 12 * 4) % 12 - 7;	/* more flats */
		break;
	default:
		sf = (sf + 5 + 12 * 4) % 12 - 5;	/* Db, F# or B */
		break;
	}
	key->sf = sf;
}

/* -- 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) {
			if (pits[j] == s->as.u.key.pits[i]) {
				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;
	struct key_s okey;			/* original key */
	int i;

	if (s->as.u.key.octave != NO_OCTAVE)
		curvoice->octave = s->as.u.key.octave;
	if (s->as.u.key.cue > 0)
		curvoice->scale = 0.7;
	else if (s->as.u.key.cue < 0)
		curvoice->scale = 1;
	if (s->as.u.key.stafflines >= 0)
		curvoice->stafflines = s->as.u.key.stafflines;
	if (s->as.u.key.staffscale != 0)
		curvoice->staffscale = s->as.u.key.staffscale;

	if (s->as.u.key.empty == 1)		/* clef only */
		return;

	if (s->as.u.key.sf != 0
	 && !s->as.u.key.exp
	 && s->as.u.key.nacc != 0)
		set_acc(s);

	memcpy(&okey, &s->as.u.key, sizeof okey);
	if (s->as.state == ABC_S_HEAD)		/* if first K: (start of tune) */
		curvoice->transpose = cfmt.transpose;
	if (curvoice->transpose != 0) {
		key_transpose(&s->as.u.key);

#if 0
		/* transpose explicit accidentals */
//fixme: not correct - transpose adds or removes accidentals...
		if (s->as.u.key.nacc > 0) {
			struct VOICE_S voice, *voice_sav;
			struct SYMBOL note;

			memset(&voice, 0, sizeof voice);
			voice.transpose = curvoice->transpose;
			memcpy(&voice.ckey, &s->as.u.key, sizeof voice.ckey);
			voice.ckey.empty = 2;
			voice.ckey.nacc = 0;
			memset(&note, 0, sizeof note);
			memcpy(note.as.u.note.pits, voice.ckey.pits,
					sizeof note.as.u.note.pits);
			memcpy(note.as.u.note.accs, voice.ckey.accs,
					sizeof note.as.u.note.accs);
			note.as.u.note.nhd = s->as.u.key.nacc;
			voice_sav = curvoice;
			curvoice = &voice;
			note_transpose(&note);
			memcpy(s->as.u.key.pits, note.as.u.note.pits,
					sizeof s->as.u.key.pits);
			memcpy(s->as.u.key.accs, note.as.u.note.accs,
					sizeof s->as.u.key.accs);
			curvoice = voice_sav;
		}
#endif
	}

	if (s->as.state == ABC_S_HEAD) {	/* start of tune */
		for (i = MAXVOICE, p_voice = voice_tb;
		     --i >= 0;
		     p_voice++) {
			memcpy(&p_voice->key, &s->as.u.key,
						sizeof p_voice->key);
			memcpy(&p_voice->ckey, &s->as.u.key,
						sizeof p_voice->ckey);
			memcpy(&p_voice->okey, &okey,
						sizeof p_voice->okey);
			if (s->as.u.key.octave != NO_OCTAVE)
				p_voice->octave = s->as.u.key.octave;
			if (s->as.u.key.stafflines >= 0)
				p_voice->stafflines = s->as.u.key.stafflines;
			if (s->as.u.key.staffscale != 0)
				p_voice->staffscale = s->as.u.key.staffscale;
//fixme: update parsys->voice[voice].stafflines = stafflines; ?
		}
		return;
	}

	/* ABC_S_TUNE (K: cannot be ABC_S_GLOBAL) */
	if (is_tune_sig()) {

		/* define the starting key signature */
		memcpy(&curvoice->key, &s->as.u.key,
					sizeof curvoice->key);
		memcpy(&curvoice->ckey, &s->as.u.key,
					sizeof curvoice->ckey);
		memcpy(&curvoice->okey, &okey,
					sizeof curvoice->okey);
		if (curvoice->key.mode >= BAGPIPE
		 && curvoice->posit.std == 0)
			curvoice->posit.std = SL_BELOW;
		curvoice->transpose = cfmt.transpose;
		if (curvoice->key.empty)
			curvoice->key.sf = 0;
		return;
	}

	/* key signature change */
	if ((!s->as.next
	  || s->as.next->type != ABC_T_CLEF)	/* if not explicit clef */
	 && curvoice->ckey.sf == s->as.u.key.sf	/* and same key */
	 && curvoice->ckey.nacc == 0
	 && s->as.u.key.nacc == 0
	 && curvoice->ckey.empty == s->as.u.key.empty
	 && cfmt.keywarn)			/* (if not key warning,
						 *  keep all key signatures) */
		return;				/* ignore */

	if (!curvoice->ckey.empty)
		s->u = curvoice->ckey.sf;	/* previous key signature */
	memcpy(&curvoice->ckey, &s->as.u.key,
				sizeof curvoice->ckey);
	memcpy(&curvoice->okey, &okey,
				sizeof curvoice->okey);
	if (s->as.u.key.empty)
		s->as.u.key.sf = 0;

	/* the key signature must appear before a time signature */
	s2 = curvoice->last_sym;
	if (s2 && s2->type == TIMESIG) {
		curvoice->last_sym = s2->prev;
		if (!curvoice->last_sym)
			curvoice->sym = NULL;
		sym_link(s, KEYSIG);
		s->next = s2;
		s2->prev = s;
		curvoice->last_sym = s2;
	} else {
		sym_link(s, KEYSIG);
	}
}

/* -- 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:
		curvoice->wmeasure = s->as.u.meter.wmeasure;
		if (is_tune_sig()) {
			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;
			parsys->nstaff = 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.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.octave != NO_OCTAVE)
		p_voice->octave = s->as.u.voice.octave;
	switch (s->as.u.voice.dyn) {
	case 1:
		p_voice->posit.dyn = SL_ABOVE;
		p_voice->posit.vol = SL_ABOVE;
		break;
	case -1:
		p_voice->posit.dyn = SL_BELOW;
		p_voice->posit.vol = SL_BELOW;
		break;
	}
	switch (s->as.u.voice.lyrics) {
	case 1:
		p_voice->posit.voc = SL_ABOVE;
		break;
	case -1:
		p_voice->posit.voc = SL_BELOW;
		break;
	}
	switch (s->as.u.voice.gchord) {
	case 1:
		p_voice->posit.gch = SL_ABOVE;
		break;
	case -1:
		p_voice->posit.gch = SL_BELOW;
		break;
	}
	switch (s->as.u.voice.stem) {
	case 1:
		p_voice->posit.std = SL_ABOVE;
		break;
	case -1:
		p_voice->posit.std = SL_BELOW;
		break;
	case 2:
		p_voice->posit.std = 0;		/* auto */
		break;
	}
	switch (s->as.u.voice.gstem) {
	case 1:
		p_voice->posit.gsd = SL_ABOVE;
		break;
	case -1:
		p_voice->posit.gsd = SL_BELOW;
		break;
	case 2:
		p_voice->posit.gsd = 0;		/* auto */
		break;
	}
	if (s->as.u.voice.scale != 0)
		p_voice->scale = s->as.u.voice.scale;
	else if (s->as.u.voice.cue > 0)
		p_voice->scale = 0.7;
	else if (s->as.u.voice.cue < 0)
		p_voice->scale = 1;
	if (s->as.u.voice.stafflines >= 0)
		p_voice->stafflines = s->as.u.voice.stafflines;
	if (s->as.u.voice.staffscale != 0)
		p_voice->staffscale = s->as.u.voice.staffscale;

	set_tblt(p_voice);

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

/* sort the notes of the chord by pitch (lowest first) */
void sort_pitch(struct SYMBOL *s, int combine)
{
	int i, nx, k;
	unsigned char new_order[MAXHD], inv_order[MAXHD];

	for (i = 0; i <= s->nhd; i++)
		new_order[i] = i;
	for (;;) {
		nx = 0;
		for (i = 1; i <= s->nhd; i++) {
			if (s->as.u.note.pits[i] >= s->as.u.note.pits[i-1])
				continue;
#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);
#undef xch
			k = new_order[i];
			new_order[i] = new_order[i-1];
			new_order[i-1] = k;
			if (combine) {
				k = s->pits[i];
				s->pits[i] = s->pits[i-1];
				s->pits[i-1] = k;
			}
			nx++;
		}
		if (nx == 0)
			break;
	}

	/* change the indexes of the note head decorations */
	if (s->nhd > 0) {
		for (i = 0; i <= s->nhd; i++)
			inv_order[new_order[i]] = i;
		for (i = 0; i <= s->nhd; i++) {
			k = s->as.u.note.dc.tm[i].m;
			if (k != 255)
				s->as.u.note.dc.tm[i].m = inv_order[k];
		}
	}
}

/* -- 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 (curvoice->octave != 0) {
		for (i = 0; i <= m; i++)
			s->as.u.note.pits[i] += curvoice->octave * 7;
	}

	if (curvoice->perc)
		s->sflags |= S_PERC;
	else if (s->as.type == ABC_T_NOTE
	      && curvoice->transpose != 0)
		note_transpose(s);

	if (!(s->as.flags & ABC_F_GRACE)) {
		switch (curvoice->posit.std) {
		case SL_ABOVE: s->stem = 1; break;
		case SL_BELOW: s->stem = -1; break;
		}
	} else {			/* grace note - adjust its duration */
		int div;

		if (curvoice->key.mode < BAGPIPE) {
			div = 2;
			if (!prev
			 || !(prev->as.flags & ABC_F_GRACE)) {
				if (s->as.flags & ABC_F_GR_END)
					div = 1;	/* one grace note */
			}
		} else {
			div = 4;
		}
		for (i = 0; i <= m; i++)
			s->as.u.note.lens[i] /= div;
		s->dur /= div;
		switch (curvoice->posit.gsd) {
		case SL_ABOVE: s->stem = 1; break;
		case SL_BELOW: s->stem = -1; break;
		case SL_HIDDEN:	s->stem = 2; break;	/* opposite */
		}
	}

	/* convert the decorations
	 * (!beam-accel! and !beam-rall! may change the note duration) */
	if (s->as.u.note.dc.n > 0)
		deco_cnv(&s->as.u.note.dc, s, prev);

	/* insert the note/rest in the voice */
	sym_link(s,  s->as.u.note.lens[0] != 0 ? NOTEREST : SPACE);
	if (!(s->as.flags & ABC_F_GRACE))
		curvoice->time += s->dur;
	s->nohdi1 = s->nohdi2 = -1;

	/* 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;
		}
	} else {

		/* sort the notes of the chord by pitch (lowest first) */
		sort_pitch(s, 0);
	}

	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)) {		/* if 2nd note - see deco.c */
			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;
	}

	switch (cfmt.shiftunison) {
	case 0:
		break;
	case 1:
		s->sflags |= S_SHIFTUNISON_1;
		break;
	case 2:
		s->sflags |= S_SHIFTUNISON_2;
		break;
	default:
		s->sflags |= S_SHIFTUNISON_1 | S_SHIFTUNISON_2;
		break;
	}

	/* build the guitar chords */
	if (s->as.text)
		gch_build(s);
}

/* -- treat a postscript definition -- */
static void ps_def(struct SYMBOL *s,
			char *p,
			char use)	/* cf user_ps_add() */
{
	if (!svg && epsf <= 1) {	/* if PS output */
		if (secure
		 || use == 'g'
		 || use == 's')
			return;
	} else {			/* if SVG output */
		if (use == 'p'
		 || (use == 'g' && file_initialized > 0))
			return;
	}
	if (s->as.prev)
		s->as.state = s->as.prev->state;
	if (s->as.state == ABC_S_TUNE) {
		if (use == 'g')
			return;
		sym_link(s, FMTCHG);
		s->u = PSSEQ;
		s->as.text = p;
//		s->as.flags |= ABC_F_INVIS;
		return;
	}
	if (file_initialized > 0 || mbf != outbuf)
		a2b("%s\n", p);
	else
		user_ps_add(p, use);
}

/* get a symbol selection */
/* measure_number [ ":" time_numerator "/" time_denominator ] */
static char *get_symsel(struct symsel_s *symsel, char *p)
{
	char *q;
	int tn, td, n;

	symsel->bar = strtod(p, &q);
	if (*q >= 'a' && *q <= 'z')
		symsel->seq = *q++ - 'a';
	else
		symsel->seq = 0;
	if (*q == ':') {
		if (sscanf(q + 1, "%d/%d%n", &tn, &td, &n) != 2
		 || td <= 0)
			return 0;
		symsel->time = BASE_LEN * tn / td;
		q += 1 + n;
	} else {
		symsel->time = 0;
	}
	return q;
}

/* free the voice options */
static void free_voice_opt(struct voice_opt_s *opt)
{
	struct voice_opt_s *opt2;

	while (opt) {
		opt2 = opt->next;
		free(opt);
		opt = opt2;
	}
}

/* get a transposition */
static int get_transpose(char *p)
{
	int val, pit1, pit2, len, acc, nostem;
	static int pit_st[7] = {0, 2, 4, 5, 7, 9, 11};

	if (isdigit(*p) || *p == '-' || *p == '+') {
		sscanf(p, "%d", &val);
		val *= 3;
		switch (p[strlen(p) - 1]) {
		default:
			return val;
		case '#':
			val++;
			break;
		case 'b':
			val += 2;
			break;
		}
		if (val > 0)
			return val;
		return val - 3;
	}

	// by music interval
	p = parse_basic_note(p, &pit1, &len, &acc, &nostem);
	pit1 += 126 - 2;    // for value > 0 and 'C' % 7 == 0
	val = (pit1 / 7) * 12 + pit_st[pit1 % 7];
	switch (acc) {
	case A_DS:
		val += 2;
		break;
	case A_SH:
		val++;
		break;
	case A_FT:
		val--;
		break;
	case A_DF:
		val -= 2;
		break;
	}
	pit1 = val;
	p = parse_basic_note(p, &pit2, &len, &acc, &nostem);
	pit2 += 126 - 2;
	val = (pit2 / 7) * 12 + pit_st[pit2 % 7];
	switch (acc) {
	case A_DS:
		val += 2;
		break;
	case A_SH:
		val++;
		break;
	case A_FT:
		val--;
		break;
	case A_DF:
		val -= 2;
		break;
	}
	pit2 = val;

	val = (pit2 - pit1) * 3;
	switch (acc) {
	default:
		return val;
	case A_DS:
	case A_SH:
		val++;
		break;
	case A_FT:
	case A_DF:
		val += 2;
		break;
	}
	if (val > 0)
		return val;
	return val - 3;
}

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

	p = as->text + 2;		/* skip '%%' */
	q = p + strlen(p) - 5;
	lock = strncmp(q, " lock", 5) == 0;
	if (lock)
		*q = '\0'; 
	p = get_str(w, p, sizeof w);
	if (as->state == ABC_S_HEAD
	 && !check_header(as)) {
		error(1, s, "Cannot have %%%%%s in tune header", w);
		return as;
	}
	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 = as->text + 2 + 7;
			while (*p != '\0' && *p != '\n')
				p++;
			if (*p == '\0')
				return as;		/* empty */
			ps_def(s, p + 1, use);
			return as;
		}
		if (strcmp(w, "begintext") == 0) {
			int job;

			if (as->state == ABC_S_TUNE) {
				gen_ly(1);
			} else if (as->state == ABC_S_GLOBAL) {
				if (epsf || in_fname == 0)
					return as;
			}
			p = as->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 as;	/* empty */
			} else {
				job = cfmt.textoption;
			}
			if (job != T_SKIP) {
				p++;
				write_text(w, p, job);
			}
			return as;
		}
		if (strcmp(w, "break") == 0) {
			struct brk_s *brk;

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

			/* %%clip <symbol selection> "-" <symbol selection> */
			if (*p != '-') {
				p = get_symsel(&clip_start, p);
				if (!p) {
					error(1, s, "Bad start in %%%%%s", w);
					return as;
				}
				if (*p != '-') {
					error(1, s, "Lack of '-' in %%%%%s", w);
					return as;
				}
			}
			p++;
			p = get_symsel(&clip_end, p);
			if (!p) {
				error(1, s, "Bad end in %%%%%s", w);
				return as;
			}
			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 as;
		}
		break;
	case 'd':
		if (strcmp(w, "deco") == 0) {
			deco_add(p);
			return as;
		}
		if (strcmp(w, "dynamic") == 0) {
			set_voice_param(curvoice, as->state, w, p);
			return as;
		}
		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 as;
			get_str(line, p, sizeof line);
			if ((fp = open_file(line, "eps", fn)) == NULL) {
				error(1, s, "No such file: %s", line);
				return as;
			}

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

			generate();
			if (strncmp(p, "start", 5) == 0) {
				if (!in_page)
					a2b("%%\n");	/* initialize the output */
				buffer_eob();
				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();
					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();
					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();
					if (!info['X' - 'A']
					 && !epsf)
						write_buffer();
				}
			} else {
				error(1, s,
				      "Unknown keyword '%s' in %%%s", p, w);
			}
			return as;
		}
		break;
	case 'n':
		if (strcmp(w, "newpage") == 0) {
			if (epsf || in_fname == 0)
				return as;
			if (as->state == ABC_S_TUNE)
				generate();
			buffer_eob();
			write_buffer();
			use_buffer = 0;
			if (isdigit((unsigned char) *p))
				pagenum = atoi(p);
			close_page();
			if (as->state == ABC_S_TUNE)
				bskip(cfmt.topspace);
			return as;
		}
		break;
	case 'p':
		if (strcmp(w, "ps") == 0
		 || strcmp(w, "postscript") == 0) {
			ps_def(s, p, 'b');
			return as;
		}
		break;
	case 'o':
		if (strcmp(w, "ornament") == 0) {
			set_voice_param(curvoice, as->state, w, p);
			return as;
		}
		break;
	case 'r':
		if (strcmp(w, "repbra") == 0) {
			if (as->state != ABC_S_TUNE)
				return as;
			curvoice->norepbra = strchr("0FfNn", *p)
						|| *p == '\0';
			return as;
		}
		if (strcmp(w, "repeat") == 0) {
			int n, k;

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

				n = atoi(p);
				for (as2 = as->next; as2; as2 = as2->next) {
					if (as2->type == ABC_T_BAR) {
						s = (struct SYMBOL *) as2;
						s->u = n;
						break;
					}
				}
				return as;
			}
			strcpy(w, "measurefirst");
			break;
		}
		if (strcmp(w, "sep") == 0) {
			float h2, len, lwidth;

			if (as->state == ABC_S_TUNE) {
				gen_ly(0);
			} else if (as->state == ABC_S_GLOBAL) {
				if (epsf || in_fname == 0)
					return as;
			}
			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);
			a2b("%.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)
				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 > (unsigned) nstaff) {
				error(1, s, "Bad staff in %%%s", w);
				return as;
			}
			curvoice->floating = 0;
			curvoice->cstaff = staff;
			return as;
		}
		if (strcmp(w, "staffbreak") == 0) {
			if (as->state != ABC_S_TUNE)
				return as;
			if (isdigit(*p)) {
				s->xmx = scan_u(p);
				if (s->xmx < 0) {
					error(1, s, "Bad value in %%%s", w);
					return as;
				}
				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 as;
		}
		if (strcmp(w, "stafflines") == 0) {
			int voice, lines;

			lines = atoi(p);
			if ((unsigned) lines >= 10) {
				error(1, s, "Bad value in %%%s", w);
				return as;
			}
			if (as->state != ABC_S_TUNE) {
				for (voice = 0; voice < MAXVOICE; voice++)
					voice_tb[voice].stafflines = lines;
			} else {
				curvoice->stafflines = lines;
			}
			return as;
		}
		if (strcmp(w, "staffcolor") == 0) {
			int color;

			if (sscanf(p, "#%06x", &color) != 1
			 || (unsigned) color > 0x00ffffff) {
				error(1, s, "Bad color in %%%%staffcolor");
				return as;
			}
			sym_link(s, FMTCHG);
			s->u = STAFF_COLOR;
			s->as.u.length.base_length = color;	// ugly!
			return as;
		}
		if (strcmp(w, "staffscale") == 0) {
			int voice;
			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 as;
			}
			if (as->state != ABC_S_TUNE) {
				for (voice = 0; voice < MAXVOICE; voice++)
					voice_tb[voice].staffscale = scale;
			} else {
				curvoice->staffscale = scale;
			}
			return as;
		}
		if (strcmp(w, "staves") == 0
		 || strcmp(w, "score") == 0) {
			if (as->state == ABC_S_GLOBAL)
				return as;
			get_staves(s);
			return as;
		}
		if (strcmp(w, "stemdir") == 0) {
			set_voice_param(curvoice, as->state, w, p);
			return as;
		}
		if (strcmp(w, "sysstaffsep") == 0) {
			if (as->state != ABC_S_TUNE)
				break;
			parsys->voice[curvoice - voice_tb].sep = scan_u(p);
			return as;
		}
		break;
	case 't':
		if (strcmp(w, "text") == 0) {
			int job;

center:
			if (as->state == ABC_S_TUNE) {
				gen_ly(1);
			} else if (as->state == ABC_S_GLOBAL) {
				if (epsf || in_fname == 0)
					return as;
			}
			if (w[0] == 'c') {
				job = T_CENTER;
			} else {
				job = cfmt.textoption;
				switch(job) {
				case T_SKIP:
					return as;
				case T_LEFT:
				case T_RIGHT:
				case T_CENTER:
					break;
				default:
					job = T_LEFT;
					break;
				}
			}
			write_text(w, p, job);
			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_HEAD:
				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]) {
					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 as;
		}
		if (strcmp(w, "transpose") == 0) {
			struct SYMBOL *s2;
			int val;

			val = get_transpose(p);
			switch (as->state) {
			case ABC_S_GLOBAL:
				cfmt.transpose = val;
				return as;
			case ABC_S_HEAD: {
				struct VOICE_S *p_voice;
				int i;

				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 as;
			    }
			}
			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 as;
			}
			for (;;) {
				if (s2->type == KEYSIG)
					break;
				if (s2->time == curvoice->time) {
					s2 = s2->prev;
					if (s2)
						continue;
				}
				s2 = s;
				s2->as.type = ABC_T_INFO;
				s2->as.text = (char *) getarena(2);
				s2->as.text[0] = 'K';
				s2->as.text[1] = '\0';
				sym_link(s2, KEYSIG);
				s2->u = curvoice->ckey.sf;
				break;
			}
			memcpy(&s2->as.u.key, &curvoice->okey,
						sizeof s2->as.u.key);
			key_transpose(&s2->as.u.key);
			memcpy(&curvoice->ckey, &s2->as.u.key,
						sizeof curvoice->ckey);
			if (curvoice->key.empty)
				s2->as.u.key.sf = 0;
			return as;
		}
		if (strcmp(w, "tune") == 0) {
			struct abcsym *as2, *as3;
			struct tune_opt_s *opt, *opt2;

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

			/* 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 as;
			}

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

			/* search the end of the tune options */
			as2 = as;
			for (;;) {
				as3 = as2->next;
				if (!as3)
					break;
				if (as3->type != ABC_T_NULL
				 && (as3->type != ABC_T_PSCOM
				  || strncmp(&as3->text[2], "tune ", 5) == 0))
					break;
				as2 = as3;
			}

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

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

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

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

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

			/* 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 as;
			}

			if (strcmp(p, "end") == 0)
				return as;	/* 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 */
			as2 = as;
			for (;;) {
				struct abcsym *as3;

				as3 = as2->next;
				if (!as3)
					break;
				if (as3->type != ABC_T_NULL
				 && (as3->type != ABC_T_PSCOM
				  || strncmp(&as3->text[2], "score ", 6) == 0
				  || strncmp(&as3->text[2], "staves ", 7) == 0
				  || strncmp(&as3->text[2], "tune ", 5) == 0
				  || strncmp(&as3->text[2], "voice ", 6) == 0))
					break;
				as2 = as3;
			}

			/* if already the same %%voice
			 * remove the options */
			opt2 = NULL;
			for ( ; opt; opt = opt->next) {
				if (strcmp(opt->s->as.text, as->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 (as2 == as)		/* no option */
				return as;
			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 = s;
			for ( ; as != as2; as = as->next) {
				if (as->next->type != ABC_T_PSCOM)
					continue;
				as->next->state = ABC_S_TUNE;
				s->next = (struct SYMBOL *) as->next;
				s = s->next;
			}
			return as;
		}
		if (strcmp(w, "voicecolor") == 0) {
			int color;

			if (sscanf(p, "#%06x", &color) != 1
			 || (unsigned) color > 0x00ffffff) {
				error(1, s, "Bad color in %%%%voicecolor");
				return as;
			}
			sym_link(s, FMTCHG);
			s->u = VOICE_COLOR;
			s->as.u.length.base_length = color;	// ugly!
			return as;
		}
		if (strcmp(w, "voicescale") == 0) {
			int voice;
			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 as;
			}
			if (as->state != ABC_S_TUNE) {
				for (voice = 0; voice < MAXVOICE; voice++)
					voice_tb[voice].scale = scale;
			} else {
				curvoice->scale = scale;
			}
			return as;
		}
		if (strcmp(w, "volume") == 0) {
			set_voice_param(curvoice, as->state, w, p);
			return as;
		}
		if (strcmp(w, "vskip") == 0) {
			if (as->state == ABC_S_TUNE) {
				gen_ly(0);
			} else if (as->state == ABC_S_GLOBAL) {
				if (epsf || in_fname == 0)
					return as;
			}
			bskip(scan_u(p));
			buffer_eob();
			return as;
		}
		break;
	}
	if (as->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 as;
}

/* -- set the duration of notes/rests in a tuplet -- */
/*fixme: KO if voice change*/
/*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; 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; as2 = as2->next) {
				switch (as2->type) {
				case ABC_T_NOTE:
				case ABC_T_REST:
					break;
				case ABC_T_EOLN:
					if (as2->u.eoln.type != 1) {
						error(1, t,
							"End of line found inside a nested tuplet");
						return;
					}
					continue;
				default:
					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;
		}
		switch (as->type) {
		case ABC_T_NOTE:
		case ABC_T_REST:
			break;
		case ABC_T_EOLN:
			if (as->u.eoln.type != 1) {
				error(1, t, "End of line found inside a tuplet");
				return;
			}
			continue;
		default:
			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) {
		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; 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);
	curvoice = p_voice2;
	if (p_voice->second)
		s->sflags |= S_SECOND;
	if (p_voice->floating)
		s->sflags |= S_FLOATING;
	if (s->prev) {
		s->as.fn = s->prev->as.fn;
		s->as.linenum = s->prev->as.linenum;
		s->as.colnum = s->prev->as.colnum;
	}
	return s;
}

/* -- link a ABC symbol into the current 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) {
		s->prev = p_voice->last_sym;
		if (s->prev)
			p_voice->last_sym->next = s;
		else
			p_voice->sym = s;
		p_voice->last_sym = s;
//fixme:test bug
//	} else {
//		if (p_voice->sym)
//			p_voice->last_sym = p_voice->sym = s;
	}

	s->type = type;
	s->voice = p_voice - voice_tb;
	s->staff = p_voice->cstaff;
	s->time = p_voice->time;
	s->posit = p_voice->posit;
}
abcm2ps-8.5.2/sample.abc0000644000175000017500000000666312204130373013105 0ustar  jefjef% Sample file to test various features of abc2ps

U: M = !tenuto!		% abcm2ps: default M is lowermordent

% abcm2ps >= 7.6.0 - definition of '...' (ellipsis) for PostScript
%%glyph 2026 ellipsis

X:1
T:Scale
T:Second Title
T:Third Title
M:C
%K:C		% abcm2ps: automatic clef processing gives a bass clef
K:C treble
L: 1/4
 "C,"C,"D,"D,"E,"E,"F,"F,  "G,"G,"A,"A,"B,"B,\
| "C"C"D"D"E"E"F"F  "G"G"A"A"B"B| "c"c "d"d"e"e"f"f  "g"g"a"a"b"b"c'"c' |

X:3
T:Short and Long Notes, Beams and Tuplets
C:Trad
M:C
K:C
L: 1/8
 c8| d4 e2 fg | C c C/ c/ d/e/ d// C// C/D/E/F/ | d>e d>>e e<fe (3CDE (4fgab |

X:4
T:Key signature, Accidentals and Decorations
M:C
K:A
L: 1/4
 ^C=C_C ^G=G_G | .F/.G/.A/ Ma/Mg/Mf/ Jc J^c J[c^f] J[c^g]  |\
 {f}e {C}D {cd}c {E^c}a2 {dedc}d|    
 uc vc'-c'/Mb/Mc'/Ma/ | (b4 | b/).a/.b/.c'/ | ~A ~g \
% RA Rg MA Mg | .A .g vf/-g/ (u.a/.b/)  | uR~M.c2  Hg4 || % abcm2ps: '-' is not a slur
 RA Rg MA Mg | .A .g (vf/g/) (u.a/.b/)  | uR~M.c2  Hg4 ||

X:5
T:Bars and Rests
M:6/8
L: 1/4
K:D
[| z4 |] z2 |: z z/z// :| z2> :: z2 z>z | f>z g>z ||

X:6
T:Chords, Unions, First and Second Endings
M:9/8
K:D
L: 1/4
%|: [D2FA] ~+d2fa+ | [c2e][df][eg]|\	% abcm2ps: '+' is no more handled
|: [D2F2A2] ~[d2f2a2] | [c2e2][df][eg]|\
 [cc] [dd] [F/A][G/B][D/F]>[C/E] [G/e][A/e] [G/e]>.[A/e][B/e]>.[c/e]\
|1 (3[A//df][A//ef][A//cg] [G3Ce] :|2 (3[A//=df][^A//dg][_A//_c^g] [e3gc] |]

X:7
T:Slurs and Ties
T: Title with funny chars like \005 Çéñô Àçäßö © …
M:C|
K:Ebm
[| (CDEF) ((3efg) ((3gag)| (C2 EF) (ef(ga)) | ((c2 (3(d)ef) e2)\
 A2-|A4 c4-|(c4(e4)|a8) |]

X:8
T:Changing Time or Key Signatures, Guitar Chords
M: 6/8
K: G
 "Em"ABc def |\
M: 9/8
% abcm2ps: '\' is no more handled
%"Am7"A,CC DFF GBB |\M:4/4\"G"a,2b,2"D"c2d2 ||\L:1/4\\K:Bb\"Eb"e4|
"Am7"A,CC DFF GBB |[M:4/4]"G"a,2b,2"D"c2d2 ||[L:1/4][K:Bb]"Eb"e4-|
K: Gb
M: 6/8
L:1/8		% abcm2ps: basic length is not reset on M:
%| -"Gb"ede edc | def fed |1 "A"efg "D"gfe | e6 \ % abcm2ps: cannot have '-' before a note
| "Gb"ede edc | def fed |1 "A"efg "D"gfe | e6 \
:|2 "G"gag "F"f"Em"e"D7"d | "C"c6 |]

X:9
T:Strange tuplet cases
M:C
K:C
L: 1/8
 (3cde c(3d/e/f/ |(3zcd (3z/c/d/ (3czc c(4d/e/f/z/ d2-(3def | \
   (3::2 c4d2  (3::4 cde/f/ (3gfe |
 (3z2G,2A,2 (3C2E2G2 |e-(5e//f//g//f//g// de- (5e//f//g//f//g// |\
  (6z/c/e/g/e/c/ (6z/c/e/g/e/c/ | (3d/e/f/g-(3g/f/e/d || 

X:10
T:Chords with many accidentals
M: 6/8
K: G
[^c^d] [^c^e] [^c^f] [^c^g] [^c^a] [^c^b] |\
[^C^D] [^C^E] [^C^F] [^C^G] [^C^A] [^C^B] |\
[^c^d^e] [^c^d^f] [^c^f^g] [^c^f^a] [^c=d=f^g_a_b]  |]
[^c^f^a] [^c^f^b] [^c^f^c'] [^c^f^d'] [^c^f^e'] |\
[^c^e^f] [^c^f^g] [^c^g^a] [^c^g^b] |\
 [^c^d^c'] [^c^e^c'] [^A^e^c']   [^G^e^c']   \
 [^c_e^g][^c^f^g][^c^g^a][^c^d^g][^G^e^c']   ||

X:11
T:Horizontal beams
M:9/8
K:C
L: 1/8
 c,d,c, d,e,d, e,f,e, b,cb,  | c,/d,/c,/ d,/e,/d,/ e,/f,/e,/ |\
   c,//d,//c,// d,//e,//d,// e,//f,//e,// |
 cdc ded efe b,a,b,| c/d/c/ d/e/d/ e/f/e/ | c//d//c// d//e//d// e//f//e// |
 c'd'c' d'e'd' e'f'e' f'g'f' | c'/d'/c'/ d'/e'/d'/ e'/f'/e'/ |\ 
   c'//d'//c'// d'//e'//d'// e'//f'//e'// |

X:12
T:Gracenotes
L:1/8
M:C
K:D
FA{c}AF DF{^dc}A f{A}df f{AGA}df |{B}D2 {A}D2 {G}D2 {F}D2 {E}D2 |\
 {E}c2 {F}c2 {G}c2 {A}c2 {B}c2 | 
   {A}^c2 {gcd}c2 {gAGAG}A2{g}c<{GdG}e {Gdc}d>c {gBd}B<{e}G |\ 
  {G}[G4e4] {FGAB}[^c4A4] {ef}[e4c4] {d'c'bagfedcB_AcBFGC}D4| 

X: 13
T: Vocals
M: C|
K: F
L: 1/4
BA |: "Gm"G2AB|"C7"cd2e|"F"f2fe|"Dm"dA2d|
w: Close your eyes and I'll kiss you, to-mor-row I'll miss you; re-
"Bb"d2dc|"Gm"B2GF|"Eb"G4-|G2 z2|
w:mem-ber I'll al-ways be true._ 
abcm2ps-8.5.2/sample2.abc0000644000175000017500000000571612150576471013202 0ustar  jefjef% Sample file to test various features of abcm2ps
%%footer abcm2ps - sample2

U: N = !tenuto!

X:1
T:All clefs
M:C
L:1/4
K:C bass
"^bass"G,CEG|[K:bass3]"^bass3"G,CEG|[K:alto4]"^alto4"G,CEG|[K:alto]"^alto"G,CEG|
[K:alto2]"^alto2"G,CEG|[K:alto1]"^alto1"G,CEG|[K:treble]"^treble"G,CEG|
[K:treble1]"^treble1"G,CEG|[K:treble-8]"^treble-8"G,CEG|[K:treble+8]"^treble+8"G,CEG|

X:2
T:Key signature change
T:and multi-measure rest
M:2
L:1/4
K:C
Z4|"C"CEGc|[K:A]"A"Acea|[K:B]"B"Bdfb|[K:A]"A"Acea|
[K:Eb]"Eb"EGBe|[K:Cb]"Cb"CEGc|[K:C]"C"CEGc|

X:3
T:All clefs with max signatures
M:C
L:1/4
K:C# bass
C,E,G,C|[K:Cb]C,E,G,C|[K:C# bass3]C,E,G,C|
[K:Cb]C,E,G,C|[K:C# alto4]G,CEG|[K:Cb]G,CEG|
[K:C# alto]G,CEG|[K:Cb]G,CEG|[K:C# alto2]CEGc|
[K:Cb]CEGc|[K:C# alto1]CEGc|[K:Cb]CEGc|
[K:C# treble]CEGc|[K:Cb]CEGc|[K:C]CEGc|

X:4
T:Guitar chords - annotations
M:none
L:1/4
K:C
"^no time""^signature"CD"gchord""^on bar"|EF\
"^appogiattura"{B}c "^acciaccatura"{/B}c \
"^three;annot;lines"G "^and""^four""^annot""^lines!"c| \
"^Fa#"^F "^Sib"_B "^Fa="=F \
"F#"^F "Bb"_B||

X:5
T:Standard decorations
M:none
L:1/8
K:C
~C.D JENF HCRD TEuF vcLB MAPG ScOB|
w: \~ . J N H R T u v L M P S O
w: grace dot slide tenuto fermata roll trill upbow downbow \
w: emphasis lmordent umordent segno coda

X:6
T:All decorations
M:none
L:1/8
K:C
!0!C!1!D !2!E!3!F !4!G!5!A !+!B!accent!c|\
w:~0 ~1 ~2 ~3 ~4 ~5 ~+ accent
!breath!C!crescendo(!D !crescendo)!E!D.C.!F !diminuendo(!G!diminuendo)!A !f!B!ffff!c|
w:breath crescendo( crescendo) D.C. diminuendo( diminuendo) ~f ffff
!fine!C!invertedfermata!D !longphrase!E !mediumphrase!F !mf!G!open!A !p!B!pppp!c|
w:fine invertedfermata longphrase mediumphrase mf open ~p pppp
!pralltriller!C!sfz!D !shortphrase!E !snap!F !thumb!G!turn!A!wedge!B!D.S.!c|
w:pralltriller sfz shortphrase snap thumb turn wedge D.S.

X:7
T:Non standard decorations
C:Composer
O:Origin
R:Rhythm
M:none
L:1/8
K:C
!turnx!G!invertedturn!A !invertedturnx!B !arpeggio![EGc]|\
w:turnx invertedturn invertedturnx arpeggio
!trill(!c4-|!trill)!c3|
w:trill( trill)

X:8
T:Decorations on two voices
T:(also in 'd:' lines)
%%infoline 1
C:Composer
O:Origin
R:Rhythm
M:C
%%staves (1 2)
K:C
V:1
  ~c.dJeNf cdef|aabc' gabc'|!coda!cdef gfec||
d: * * * * HRTu|!mf!       |!sfz!  *** ***!D.S.!
V:2
   CDEF    CDEF|ffga   efga|C  D  EF   [EG]FEC||
d: ~.JN    HRTu|~.JN   HRTu|!5!!4!M*   !5! M
d:             |           |*  P  !3!  !4!

X:9
T:Beams
L:1/16
M:4/4
K:C
(3CDE(3FGA B/c/d/e/d/c/B/A/ (3zDE(3FGz z/c/d/e/d/c/B/z/|(3CDz(3zGA B/c/d/z/z/c/B/A/ G8|

X:10
T:Voice overlap
T:invisible and dashed bars
M:2/4
L:1/8
%%staves (1 2)
K:C
V:1
FEDC:GGGG|G2 G2|c4[|]GABc|
V:2
GABc:FEDC|GD G>D|cBAG[|]G4|

X:11
T:Clef transpositions
M:C
L:1/4
K:C
%%titleleft 1
T:No transposition
"^clef=treble""A,"A,"B,"B,"C"C"D"D|\
[K:alto]"^alto""A,"A,"B,"B,"C"C"D"D|\
[K:bass]"^bass""A,"A,"B,"B,"C"C"D"D|
T:abc2ps compatible clef transposition
%%abc2pscompat 1
[K:treble]"^treble""A,"A,"B,"B,"C"C"D"D|\
[K:alto]"^alto""A"A"B"B"c"c"d"d|\
[K:bass]"^bass""a"a"b"b"c'"c'"d'"d'|
%%titleleft 0
abcm2ps-8.5.2/sample3.abc0000644000175000017500000000414412321201413013152 0ustar  jefjef%%deco alcoda 3 dacs 20 0 0 Al Coda
%%header "		$T\n		Page $P"
%%footer "	$F"

X:1
T:abcm2ps version 3 features
M:2/4 3/4
K:C
C2D2 E2F2G2{FG}|G4-G6|1.2[M:5/4 (2/4 3/4)]G4{AB}z6:|["last time"A2B2 c6||
%%multicol start
%%rightmargin 11.5cm
[M:2/4]C2G2|C4|
%%multicol new
%%leftmargin 11.5cm
%%begintext
%%
Multi column text on the right after a blank line.
%%endtext
%%multicol end
%%multicol start
%%rightmargin 11.5cm
%%begintext
         And now at the left side.
Here, there are spaces to align
                the text on the right.
%%endtext
%%multicol new
%%leftmargin 11.5cm
CD EF|GA Bc|
%%multicol end
%
%%center Scale change inside the tune
%%scale 0.6
%%leftmargin 7cm
%%rightmargin 11cm
CD EF|
%%scale 1.2
%%leftmargin 11cm
%%rightmargin 4cm
CD EF|
%%leftmargin 5cm
%%scale 1
%%EPS sample3.eps
%%scale 0.75		% set the scale back to default
% restore the left and right margins
%%leftmargin 1.78cm
%%rightmargin 1.78cm
%%center Is this centered?

X:2
T:Staff break
M:none
L:1/8
K:G exp ^F ^F, clef=C
%%staffbreak 0.2cm
[K:G clef=treble][M:C]SDE FG|AG !alcoda!FEO|DE FES||\
%%staffbreak 2cm
[K:G clef=treble]OC2D2||

X:3
T:Staff break multi-staves
M:none
L:1/8
%%staves {1 2}
K:G
V:1
[K:G exp ^F ^F, clef=C]
%%staffbreak 0.2cm
V:2 bass
%%staffbreak 0.2cm
V:1
[K:G clef=treble][M:C]SDE FG|AG !alcoda!FEO|DE FES||\
V:2
[K:G clef=bass][M:C]D,4|A,4|D,4||\
V:1
%%staffbreak 2cm
[K:G clef=treble]OC2D2||
V:2
%%staffbreak 2cm
[K:G clef=bass]A,,2D,,2||

X:4
T:Voice overlay and chords
M:C
L:1/4
K:C
[C2G]A[B3/G2]c/|cdef&ABcd|GA{[FA]}[GB][Ac]|defg&Bcde|
GABc|efga&FGAB&CDEF|GA(&Bc|de&FG|AB&)cd|

X:5
T:Annotations and guitar chords
M:C
L:1/8
%%gchordbox 1
K:C
"^annotation""gchord in box"CD"_below"EG "<left"cd">right"ec|\
"@20,-50anywhere"gf"<("">)"ed (c4y)||

X:6
T:Grace notes
M:C
L:1/4
%%graceslurs 0
K:C
V:1 name="voice name\non\nthree lines"
{B2}A{B}A{B/}A{B//}A|{c4B4}A{c2B2}A{cB}A{c/B/}A|
{a2g2f2 e2d2c2B4}A4|\
	{(AB)}c{(AB}c){(GA)B}c{G(AB}c)|\
	(c2{BA)G}{(G/A/B/) (G/A/B/)}c2||

X:7
T:Drum
M:C
L:1/8
%%staves (1 2)
K:C clef=perc
V:1
^a^f[c^f]^f ^f^f[c^f]^f|^a^f[c^f]^f ^f/c/^f c/^a3/||
V:2
FF/F/ z3/F/ zF/F/ z/F3/|FF/F/ z3/F/ z3/F/ z/F3/||
abcm2ps-8.5.2/sample3.eps0000644000175000017500000000030107612251066013223 0ustar  jefjef%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0 0 50 40
%%Pages: 0
%%EndProlog
0 0 moveto 25 40 rlineto 25 -40 rlineto -50 0 rlineto fill
1.0 setgray /Times-Roman 14 selectfont 13 8 moveto (EPS) show
abcm2ps-8.5.2/sample4.abc0000644000175000017500000000601012443624047013166 0ustar  jefjef% sample4.abc - this file is part of abcm2ps

% definitions for tablature
%%beginps
/hn{	currentgray 1.0 setgray 1 SLW
	2 index 2 sub 2 index M 8 0 RL stroke setgray
	3 sub M /Helvetica-Bold 8 selectfont show}!
%%endps
%%deco head-3 3 hn 0 0 0 3
%%deco head-4 3 hn 0 0 0 4
%%deco head-5 3 hn 0 0 0 5
U:i=!invisible!
U:j=!head-3!
U:k=!head-4!
U:l=!head-5!

X:1
%%abc-version 2.0
T:ABC-2.0 new fea\	% strange continuation line
tures
M:C
L:1/4
%%measurenb 1
K:C
CDEF | GABc | FEDC [I:setbarnb 10] | [CEG]4 |]
s:"^annotation;in 's:' line"**+1+|||H|

X:2
T:abcm2ps-4.x.x new features
%%graceslurs 0
%%breakoneoln 0		% needed with abcm2ps-5.x.x
M:C
L:1/4
Q:1/4=ca. 80
K:none
"^slur above.."('FG) "^..below"(,FG)|\
"^dashed tie.."A.-A "^..slur".(ce/)d/"^beam;crossing;a bar"!beamon!|\
c/B/ "^chord slur"{(,G}[CA)]3||
"^standard tuplet"(3A/A/A/ "^no bracket"[I:tuplets 1 0 0](3A/A/A/ \
"^forced bracket"[I:tuplets 2 0 0](3A/A/A/ "^ratio"[I:tuplets 0 0 2](3A/A/A/| \
"^nested tuplets"[I:tuplets 2 0 2](7:8:8(3A/A/A/ A/A/A/A/A/|
"^1/4 tone;sharp"^/G ^G "^3/4 tone;sharp"^3/2G ^^G |\
"^1/4 tone;flat"_/G _G "^3/4 tone;flat"_3/2G __G|"^deco on;grace note"{.A4.B4}c4||
% tremolo: note the position of the decorations and the note lengths
"^tremolo"C/c/C/c/ C!trem1!c | C2!trem1!c2 | \
C//[Gc]//C//[Gc]// C//[Gc]//C//[Gc]// C!trem2![Gc] | C2!trem2![Gc]2 ||
%
C///D///E///F///G///F///E///D///\
C///D///E///F///G///F///E///D/// \
"^beam break"C///D///E///F///!beambr2!G///F///E///D///\
!beambr1!C///D///E///F///!beambr2!G///F///E///D///||
T:		% needed with abcm2ps-5.x.x
%%staves 1 2
%%rightmargin 12cm
V:1
"^cross staff stems"cc//c//c//c//c2 | [CEGc]2[CEGc]2||
V:2 bass
!xstem!C,C,//!xstem!C,//C,//!xstem!C,//!xstem!C,2 | !xstem![C,,C,]2[C,,C,]2||

X:3
T:Yesterday
T:(extract)
M:4/4
L:1/8
%%staves (melody1 chords melody2)
K:F
V:melody1
%%stemdir down
e3/d/ d6 | z2 d/d3/ cB/A/- AG |
w: far a-way, | Now it looks as though_ they're |
V:chords
"Dm"x4 "Dm/C"x4 | "Bb"x4  "C7"x4 |
V:melody2
%%stemdir up
%%voicescale 0.8
ie3/id/ id6 | x2 d/dc/.- c/B3/ AG |
w: used to be, | There's a shad-_ow hang-ing |

X:4
T:Fantaisie
C:Camille Saint-Saëns
M:C
L:1/8
%%staves [recit GO pos ped]
K:Eb
V:recit name="Récit"
z8|z8|z8|
V:GO treble name="Grand\nOrgue"
z2[G,B,E][I:staff 3][G,B,E] \
 [I:staff 2][A,CF][I:staff 3][A,CF] [I:staff 2][CEA][I:staff 3][CEA]| \
[I:staff 2][A,CF][I:staff 3][A,CF] [I:staff 2][B,DG][I:staff 3][B,DG] \
 [I:staff 2][CEA][I:staff 3][CEA] [I:staff 2][DAc][I:staff 3][DAc]| \
[I:staff 2][EGB][I:staff 3][EGB] [I:staff 2][GBe][I:staff 3][GBe] \
 [I:staff 2][EGB][I:staff 3][EGB] [I:staff 2][DFA][I:staff 3][DFA]|
V:pos treble name=Positif
z2x6|x8|x8|
V:ped name="Pédale"
E,,8-|E,,8-|E,,2G,,2B,,2=B,,2|

X:5
T:Special staves
M:9/8
L:1/8
%%staves [1 2] | (3 4)
K:Cm
V:1 stem=up
[CGceg]6[C-,G-,ceg]3-|[CGceg]3[CGceg]6|
% tablature
V:2 clef=none
%%stafflines 6
%%staffscale 1.6
%%staffnonote 2
[K:none][M:none]i[jGlBldkfja]6i[jGlBldkfja]3|iA3i[jGlBldkfja]6|
% percussion
V:3 clef=perc
%%stafflines 1
[K:none]^c3^c3^c^c^c|^c3^c3^c^c^c|
V:4 clef=perc stafflines=1
[K:none]A3A3A3|A6A3|
abcm2ps-8.5.2/sample5.abc0000644000175000017500000000364512366143234013200 0ustar  jefjef% sample5.abc - this file is part of abcm2ps

%%writefields NF 1
%%infoname F "This file is part of the "
F:abcm2ps sources at http://moinejf.free.fr/

X:1
%%titleformat T,TTQ-1C1A1
T:New features in abcm2ps-5.x.x
T:Subtitle one
T:Subtitle two
C:This is the composer field
A:This is the Area/Author field
N:Here are some notes (N:)
N:on two lines
M:c. C=4/4
L:1/4
Q:"Tempo in the tune header"
K:C
Q:"Tempo in the tune body"
"C"C"Dm	G"D2"C"C"^<- guitar chords between symbols"|\
	C//E//G//E// \
	[I:repeat 4 3]C//E//G//E// "^sequence repeat"C//E//G//E// C//E//G//E//|
"^measure repeat"GABc|[I:repeat 1 2]GABc|GABc|\
	G2d2|B2d2|[I:repeat 2]G2d2|B2d2|c4||
"^tremolo 2 notes"\
	z/ C//!trem1!c// C/!trem1!c/ C!trem1!c | C2!trem1!c2 | \
	z/ C//!trem2!c// C/!trem2!c/ C!trem2!c | C2!trem2!c2 |
"^tremolo 1 note"\
	z C//!/!c// c//!/!C// C/!/!c/ c/!/!C/ | \
	z C//!//!c// c//!//!C// C/!//!c/ c/!//!C/ |
	z !/!C/ !/!c/ !/!C !/!c | !/!C2 !/!c2 | \
	z !//!C/ !//!c/ !//!C !//!c | !//!C2 !//!c2 ||
	!/!C4 | !/!c4 | !//!C4 | !//!c4 |

%%writefields F 0
X:2
T:Romance sans paroles
C:F.B. Mendelssohn
M:12/8
L:1/8
K:Eb
%%staves {M A1 A2}
V:M	% Melody
x6z2z(B3|\
V:A1
%%voicescale 0.8
z(G,[B,E]) z(G,[B,E]) z(G,[B,E]) z(G,[B,E])|\
V:A2
%%staffscale 0.8
E,,3G,,3B,,3G,,3|\
%%staves M
V:M
A2GF2EE3)E3|(E3c6)(B2A|G3F3B3F3)|

X:3
T:Fugue II
T:(extract from the well-tempered clavichord II)
C:J.S. Bach
M:none
L:1/8
K:Cm
%%rightmargin 0.5cm
%%leftmargin 0.5cm
%%staves 1s 3s | {(1 2) 3}
V:1s name="Ossia"
%%staffscale 0.8
V:3s
%%staffscale 0.8
%%clef bass
V:1
gc fe/d/ e>e d=e\
V:2
B_A/G/ c/[I:staff +1]C/[I:staff -1]G- G/G/=A B/cD/\
V:3
B,/G,/ C=A,=B, CF, _B,_A,/G,/\
%%staves (1s 2) 3s | {(1 2) 3}
V:1s
|fB _e_d/c/ dG\
V:3s
|_A,_D- D/C/F/E/ D/C/D-\
V:1
|fB _ed/c/ dG\
V:2
|z/c/B/_A/ G=A BG\
V:3
|=A,=D- D/C/F/E/ D/C/_D-\
%%staves 1s 3s | {(1 2) (3 4)}
V:1
 e2-|e/e/d- d/c/=B cg de/f/|
V:2
 _AG/F/|G>F ED CeA2|
V:3
 D/C/B,/_A,/|B,=B,CD G,/C/B,/C/- C/B,/C-|
V:4
 x2|z2G,2E,2F,2|
abcm2ps-8.5.2/sample8.html0000644000175000017500000000203012443324315013402 0ustar  jefjef<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>ABC HTML formatting example</title>
</head>
<body>
<h2 align="center">ABC in HTML formatting example</h2>
<p>
This file contains 3 ABC sequences:
<ul>
	<li>a normal ABC tune
	<li>a ABC tune in a textline
	<li>2 tunes selected from an external ABC file (voice.abc)
</ul>
</p>
<p>
To generate the music, do:
<pre>	abcm2ps sample8.html -z -O Out.html</pre>
and this will create the file <code>Out.html</code>.
</p>
<div style="line-height:0">
X:1
T:An durzunell
%%infoline 1
C:Trad.
O:Brittany
M:6/8
K:Bb
B,2DF2F|FB GF2z|DGFC2D|B,3-B,2z|B,2DF2F|FB GF2z|DGFC2D|B,3-B,2z||
B,2CD2D|DE DC2z|DEFG2F|C3-C2z|B,2DF2F|FB GF2z|DGFC2D|B,6||
</div>

<p>
Beginning of 'Au clair de la lune'
<span style="vertical-align:-100%">
X:2
%%bgcolor lightblue
%%musicspace 0
M:C
L:auto
%%pagewidth 14cm
K:C
cccd|ed|cedd|c||
</span>
</p>
<div style="line-height:0">
%abc2.1
%%select 2,4
%%abc-include voices.abc
</div>
</body>
</html>
abcm2ps-8.5.2/slre.c0000644000175000017500000003403112451551335012265 0ustar  jefjef/*
 * Super Light Regular Expression library
 *
 * Copyright (c) 2012-2015 Jean-François Moine (http://moinejf.free.fr)
 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
 * All rights reserved
 *
 * "THE BEER-WARE LICENSE" (Revision 42):
 * Sergey Lyubka wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.
 */

#ifdef SLRE_TEST
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#else
#define assert(x) do {} while (0)
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "slre.h"

enum {END, BRANCH, ANY, EXACT, ANYOF, ANYBUT, OPEN, CLOSE, BOL, EOL,
	STAR, PLUS, STARQ, PLUSQ, QUEST, SPACE, NONSPACE, DIGIT};

/*
 * Commands and operands are all unsigned char (1 byte long). All code offsets
 * are relative to current address, and positive (always point forward). Data
 * offsets are absolute. Commands with operands:
 *
 * BRANCH offset1 offset2
 *	Try to match the code block that follows the BRANCH instruction
 *	(code block ends with END). If no match, try to match code block that
 *	starts at offset1. If either of these match, jump to offset2.
 *
 * EXACT data_offset data_length
 *	Try to match exact string. String is recorded in data section from
 *	data_offset, and has length data_length.
 *
 * OPEN capture_number
 * CLOSE capture_number
 *	If the user have passed 'struct cap' array for captures, OPEN
 *	records the beginning of the matched substring (cap->ptr), CLOSE
 *	sets the length (cap->len) for respective capture_number.
 *
 * STAR code_offset
 * PLUS code_offset
 * QUEST code_offset
 *	*, +, ?, respectively. Try to gobble as much as possible from the
 *	matched buffer, until code block that follows these instructions
 *	matches. When the longest possible string is matched,
 *	jump to code_offset
 *
 * STARQ, PLUSQ are non-greedy versions of STAR and PLUS.
 */

static const char *meta_chars = "|.^$*+?()[\\";

#ifdef SLRE_TEST
static struct {
	const char	*name;
	int		narg;
	const char	*flags;	
} opcodes[] = {
	{"END",		0, ""},		/* End of code block or program	*/
	{"BRANCH",	2, "oo"},	/* Alternative operator, "|"	*/
	{"ANY",		0, ""},		/* Match any character, "."	*/
	{"EXACT",	2, "d"},	/* Match exact string		*/
	{"ANYOF",	2, "D"},	/* Match any from set, "[]"	*/
	{"ANYBUT",	2, "D"},	/* Match any but from set, "[^]"*/
	{"OPEN ",	1, "i"},	/* Capture start, "("		*/
	{"CLOSE",	1, "i"},	/* Capture end, ")"		*/
	{"BOL",		0, ""},		/* Beginning of string, "^"	*/
	{"EOL",		0, ""},		/* End of string, "$"		*/
	{"STAR",	1, "o"},	/* Match zero or more times "*"	*/
	{"PLUS",	1, "o"},	/* Match one or more times, "+"	*/
	{"STARQ",	1, "o"},	/* Non-greedy STAR,  "*?"	*/
	{"PLUSQ",	1, "o"},	/* Non-greedy PLUS, "+?"	*/
	{"QUEST",	1, "o"},	/* Match zero or one time, "?"	*/
	{"SPACE",	0, ""},		/* Match whitespace, "\s"	*/
	{"NONSPACE",	0, ""},		/* Match non-space, "\S"	*/
	{"DIGIT",	0, ""}		/* Match digit, "\d"		*/
};

static void
print_character_set(FILE *fp, const unsigned char *p, int len)
{
	int	i;

	for (i = 0; i < len; i++) {
		if (i > 0)
			fputc(',', fp);
		if (p[i] == 0) {
			i++;
			if (p[i] == 0)
				fprintf(fp, "\\x%02x", p[i]);
			else
				fprintf(fp, "%s", opcodes[p[i]].name);
		} else if (isprint(p[i])) {
			fputc(p[i], fp);
		} else {
			fprintf(fp,"\\x%02x", p[i]);
		}
	}
}

void
slre_dump(const struct slre *r, FILE *fp)
{
	int	i, j, ch, op, pc;

	for (pc = 0; pc < r->code_size; pc++) {

		op = r->code[pc];
		fprintf(fp, "%3d %s ", pc, opcodes[op].name);

		for (i = 0; opcodes[op].flags[i] != '\0'; i++)
			switch (opcodes[op].flags[i]) {
			case 'i':
				fprintf(fp, "%d ", r->code[pc + 1]);
				pc++;
				break;
			case 'o':
				fprintf(fp, "%d ",
					pc + r->code[pc + 1] - i);
				pc++;
				break;
			case 'D':
				print_character_set(fp, r->data +
					r->code[pc + 1], r->code[pc + 2]);
				pc += 2;
				break;
			case 'd':
				fputc('"', fp);
				for (j = 0; j < r->code[pc + 2]; j++) {
					ch = r->data[r->code[pc + 1] + j];
					if (isprint(ch))
						fputc(ch, fp);
					else
						fprintf(fp,"\\x%02x",ch);
				}
				fputc('"', fp);
				pc += 2;
				break;
			}

		fputc('\n', fp);
	}
}
#endif /* SLRE_TEST */

static void
set_jump_offset(struct slre *r, int pc, int offset)
{
	assert(offset < r->code_size);

#if 0
	if (r->code_size - offset > 0xff)
		r->err_str = "Jump offset is too big";
	else
#endif
		r->code[pc] = (unsigned char) (r->code_size - offset);
}

static void
emit(struct slre *r, int code)
{
#if 0
	if (r->code_size >= (int) (sizeof(r->code) / sizeof(r->code[0])))
		r->err_str = "RE is too long (code overflow)";
	else
#endif
		r->code[r->code_size++] = (unsigned char) code;
}

static void
store_char_in_data(struct slre *r, int ch)
{
#if 0
	if (r->data_size >= (int) sizeof(r->data))
		r->err_str = "RE is too long (data overflow)";
	else
#endif
		r->data[r->data_size++] = ch;
}

static const char *
exact(struct slre *r, const char *re)
{
	int	old_data_size = r->data_size;

	while (*re != '\0' && strchr(meta_chars, *re) == NULL)
		store_char_in_data(r, *re++);

	emit(r, EXACT);
	emit(r, old_data_size);
	emit(r, r->data_size - old_data_size);
	return re;
}

static int
get_escape_char(char c)
{
	int	res;

	switch (c) {
	case 'n':	res = '\n';		break;
	case 'r':	res = '\r';		break;
	case 't':	res = '\t';		break;
	case '0':	res = 0;		break;
	case 'S':	res = NONSPACE << 8;	break;
	case 's':	res = SPACE << 8;	break;
	case 'd':	res = DIGIT << 8;	break;
	default:	res = c;		break;
	}

	return res;
}

static const char *
anyof(struct slre *r, const char *re)
{
	int	esc, old_data_size = r->data_size, op = ANYOF;

	if (*re == '^') {
		op = ANYBUT;
		re++;
	}

	while (*re != '\0')

		switch (*re++) {
		case ']':
			emit(r, op);
			emit(r, old_data_size);
			emit(r, r->data_size - old_data_size);
			return re;
		case '\\':
			esc = get_escape_char(*re++);
			if ((esc & 0xff) == 0) {
				store_char_in_data(r, 0);
				store_char_in_data(r, esc >> 8);
			} else {
				store_char_in_data(r, esc);
			}
			break;
		default:
			store_char_in_data(r, re[-1]);
			break;
		}

	r->err_str = "No closing ']' bracket";
	return re;
}

static void
relocate(struct slre *r, int begin, int shift)
{
	emit(r, END);
	memmove(r->code + begin + shift, r->code + begin, r->code_size - begin);
	r->code_size += shift;
}

static void
quantifier(struct slre *r, int prev, int op)
{
	if (r->code[prev] == OPEN) {
		r->err_str = "No character before *, + or ?";
		return;
	}
	if (r->code[prev] == EXACT && r->code[prev + 2] > 1) {
		r->code[prev + 2]--;
		emit(r, EXACT);
		emit(r, r->code[prev + 1] + r->code[prev + 2]);
		emit(r, 1);
		prev = r->code_size - 3;
	}
	relocate(r, prev, 2);
	r->code[prev] = op;
	set_jump_offset(r, prev + 1, prev);
}

static void
exact_one_char(struct slre *r, int ch)
{
	emit(r, EXACT);
	emit(r, r->data_size);
	emit(r, 1);
	store_char_in_data(r, ch);
}

static void
fixup_branch(struct slre *r, int fixup)
{
	if (fixup > 0) {
		emit(r, END);
		set_jump_offset(r, fixup, fixup - 2);
	}
}

static const char *
compile(struct slre *r, const char *re)
{
	int	op, esc, branch_start, last_op, fixup, cap_no, level;

	fixup = 0;
	level = r->num_caps;
	branch_start = r->code_size;
	last_op = branch_start - 2;	/* points to OPEN */

	for (;;) {
		switch (*re++) {
		case '\0':
			re--;
			return re;
		case '^':
			emit(r, BOL);
			break;
		case '$':
			emit(r, EOL);
			break;
		case '.':
			last_op = r->code_size;
			emit(r, ANY);
			break;
		case '[':
			last_op = r->code_size;
			re = anyof(r, re);
			break;
		case '\\':
			last_op = r->code_size;
			esc = get_escape_char(*re++);
			if (esc & 0xff00)
				emit(r, esc >> 8);
			else
				exact_one_char(r, esc);
			break;
		case '(':
			last_op = r->code_size;
			cap_no = ++r->num_caps;
			emit(r, OPEN);
			emit(r, cap_no);

			re = compile(r, re);
			if (*re++ != ')') {
				r->err_str = "No closing bracket";
				return re;
			}

			emit(r, CLOSE);
			emit(r, cap_no);
			break;
		case ')':
			re--;
			fixup_branch(r, fixup);
			if (level == 0)
				r->err_str = "Unbalanced brackets";
			return re;
		case '+':
		case '*':
			op = re[-1] == '*' ? STAR: PLUS;
			if (*re == '?') {
				re++;
				op = op == STAR ? STARQ : PLUSQ;
			}
			quantifier(r, last_op, op);
			break;
		case '?':
			quantifier(r, last_op, QUEST);
			break;
		case '|':
			fixup_branch(r, fixup);
			relocate(r, branch_start, 3);
			r->code[branch_start] = BRANCH;
			set_jump_offset(r, branch_start + 1, branch_start);
			fixup = branch_start + 2;
			r->code[fixup] = 0xff;
			break;
		default:
			re--;
			last_op = r->code_size;
			re = exact(r, re);
			break;
		}
	}
	return re;
}

int
slre_compile(struct slre *r, const char *re)
{
	r->err_str = NULL;
	r->code_size = r->data_size = r->num_caps = r->anchored = 0;

	if (*re == '^')
		r->anchored++;

	emit(r, OPEN);	/* This will capture what matches full RE */
	emit(r, 0);

//fixme
//	while (*re != '\0')
		compile(r, re);

	if (r->code[2] == BRANCH)
		fixup_branch(r, 4);

	emit(r, CLOSE);
	emit(r, 0);
	emit(r, END);

	return r->err_str == NULL ? 1 : 0;
}

static int match(const struct slre *, int,
		const char *, int, int *, struct cap *);

static void
loop_greedy(const struct slre *r, int pc, const char *s, int len, int *ofs)
{
	int	saved_offset, matched_offset;

	saved_offset = matched_offset = *ofs;

	while (match(r, pc + 2, s, len, ofs, NULL)) {
		saved_offset = *ofs;
		if (match(r, pc + r->code[pc + 1], s, len, ofs, NULL))
			matched_offset = saved_offset;
		*ofs = saved_offset;
	}

	*ofs = matched_offset;
}

static void
loop_non_greedy(const struct slre *r, int pc, const char *s,int len, int *ofs)
{
	int	saved_offset = *ofs;

	while (match(r, pc + 2, s, len, ofs, NULL)) {
		saved_offset = *ofs;
		if (match(r, pc + r->code[pc + 1], s, len, ofs, NULL))
			break;
	}

	*ofs = saved_offset;
}

static int
is_any_of(const unsigned char *p, int len, const char *s, int *ofs)
{
	int	i, ch;

	ch = s[*ofs];

	for (i = 0; i < len; i++)
		if (p[i] == ch) {
			(*ofs)++;
			return 1;
		}

	return 0;
}

static int
is_any_but(const unsigned char *p, int len, const char *s, int *ofs)
{
	int	i, ch;

	ch = s[*ofs];

	for (i = 0; i < len; i++) {
		if (p[i] == ch)
			return 0;
	}

	(*ofs)++;
	return 1;
}

static int
match(const struct slre *r, int pc, const char *s, int len,
		int *ofs, struct cap *caps)
{
	int	n, saved_offset, res = 1;

	while (res && r->code[pc] != END) {

		assert(pc < r->code_size);
		assert(pc < (int) (sizeof(r->code) / sizeof(r->code[0])));

		switch (r->code[pc]) {
		case BRANCH:
			saved_offset = *ofs;
			res = match(r, pc + 3, s, len, ofs, caps);
			if (res == 0) {
				*ofs = saved_offset;
				res = match(r, pc + r->code[pc + 1],
						s, len, ofs, caps);
			}
			pc += r->code[pc + 2]; 
			break;
		case EXACT:
			res = 0;
			n = r->code[pc + 2];	/* String length */
			if (n <= len - *ofs
			 && !memcmp(s + *ofs, r->data + r->code[pc + 1], n)) {
				(*ofs) += n;
				res = 1;
			}
			pc += 3;
			break;
		case QUEST:
//			res = 1;
			saved_offset = *ofs;
			if (!match(r, pc + 2, s, len, ofs, caps))
				*ofs = saved_offset;
			pc += r->code[pc + 1];
			break;
		case STAR:
//			res = 1;
			loop_greedy(r, pc, s, len, ofs);
			pc += r->code[pc + 1];
			break;
		case STARQ:
//			res = 1;
			loop_non_greedy(r, pc, s, len, ofs);
			pc += r->code[pc + 1];
			break;
		case PLUS:
			res = match(r, pc + 2, s, len, ofs, caps);
			if (res == 0)
				break;

			loop_greedy(r, pc, s, len, ofs);
			pc += r->code[pc + 1];
			break;
		case PLUSQ:
			res = match(r, pc + 2, s, len, ofs, caps);
			if (res == 0)
				break;

			loop_non_greedy(r, pc, s, len, ofs);
			pc += r->code[pc + 1];
			break;
		case SPACE:
			res = 0;
			if (*ofs < len && isspace(((unsigned char *) s)[*ofs])) {
				(*ofs)++;
				res = 1;
			}
			pc++;
			break;
		case NONSPACE:
			res = 0;
			if (*ofs <len && !isspace(((unsigned char *) s)[*ofs])) {
				(*ofs)++;
				res = 1;
			}
			pc++;
			break;
		case DIGIT:
			res = 0;
			if (*ofs < len && isdigit(((unsigned char *)s)[*ofs])) {
				(*ofs)++;
				res = 1;
			}
			pc++;
			break;
		case ANY:
			res = 0;
			if (*ofs < len) {
				(*ofs)++;
				res = 1;
			}
			pc++;
			break;
		case ANYOF:
			res = 0;
			if (*ofs < len)
				res = is_any_of(r->data + r->code[pc + 1],
					r->code[pc + 2], s, ofs);
			pc += 3;
			break;
		case ANYBUT:
			res = 0;
			if (*ofs < len)
				res = is_any_but(r->data + r->code[pc + 1],
					r->code[pc + 2], s, ofs);
			pc += 3;
			break;
		case BOL:
			res = *ofs == 0 || s[*ofs - 1] == '\n';
			pc++;
			break;
		case EOL:
			if (*ofs != len) {
				if (s[*ofs] == '\n')
					(*ofs)++;
				else
					res = 0;
			}
			pc++;
			break;
		case OPEN:
			if (caps != NULL)
				caps[r->code[pc + 1]].ptr = s + *ofs;
			pc += 2;
			break;
		case CLOSE:
			if (caps != NULL)
				caps[r->code[pc + 1]].len = (s + *ofs) -
				    caps[r->code[pc + 1]].ptr;
			pc += 2;
			break;
		case END:
			pc++;
			break;
		default:
			fprintf(stderr,
				"slre: Unknown cmd (%d) at %d\n", r->code[pc], pc);
			assert(0);
			break;
		}
	}

	return res;
}

int
slre_match(const struct slre *r, const char *buf, int len,
		struct cap *caps)
{
	int	i, ofs = 0, res = 0;

	if (r->anchored) {
		res = match(r, 0, buf, len, &ofs, caps);
	} else {
		for (i = 0; i < len && res == 0; i++) {
			ofs = i;
			res = match(r, 0, buf, len, &ofs, caps);
		}
	}

	return res;
}

#ifdef SLRE_TEST
int main(int argc, char *argv[])
{
	struct slre	slre;
	struct cap	caps[20];
	char		data[128 * 1024];
	FILE		*fp;
	int		i, count, res, len;

	if (argc < 3) {
		printf("Usage: %s 'slre' <file> [count]\n", argv[0]);
		return 1;
	}
	if (!slre_compile(&slre, argv[1])) {
		printf("Error compiling slre: %s\n", slre.err_str);
		return 1;
	}
	fp = fopen(argv[2], "rb");
	if (fp == NULL) {
		printf("Error: cannot open %s:%s\n",
			argv[2], strerror(errno));
		return 1;
	}
	slre_dump(&slre, stderr);

	memset(caps, 0, sizeof(caps));

	/* Read first 128K of file */
	len = fread(data, 1, sizeof(data), fp);
	fclose(fp);

	res = 0;
	count = argc > 3 ? atoi(argv[3]) : 1;
	for (i = 0; i < count; i++)
		res = slre_match(&slre, data, len, caps);

	printf("Result: %d\n", res);

	for (i = 0; i < 20; i++) {
		if (caps[i].len > 0)
			printf("Substring %d: [%.*s]\n", i,
				caps[i].len, caps[i].ptr);
	}

	return 0;
}
#endif /* SLRE_TEST */
abcm2ps-8.5.2/slre.h0000644000175000017500000000551011705767705012305 0ustar  jefjef/*
 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
 * All rights reserved
 *
 * "THE BEER-WARE LICENSE" (Revision 42):
 * Sergey Lyubka wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.
 */

/*
 * This is a regular expression library that implements a subset of Perl RE.
 * Please refer to http://slre.sourceforge.net for detailed description.
 *
 * Usage example (parsing HTTP request):
 *
 * struct slre	slre;
 * struct cap	captures[4 + 1];  // Number of braket pairs + 1
 * ...
 *
 * slre_compile(&slre,"^(GET|POST) (\S+) HTTP/(\S+?)\r\n");
 *
 * if (slre_match(&slre, buf, len, captures)) {
 *	printf("Request line length: %d\n", captures[0].len);
 *	printf("Method: %.*s\n", captures[1].len, captures[1].ptr);
 *	printf("URI: %.*s\n", captures[2].len, captures[2].ptr);
 * }
 *
 * Supported syntax:
 *	^		Match beginning of a buffer
 *	$		Match end of a buffer
 *	()		Grouping and substring capturing
 *	[...]		Match any character from set
 *	[^...]		Match any character but ones from set
 *	\s		Match whitespace
 *	\S		Match non-whitespace
 *	\d		Match decimal digit
 *	\r		Match carriage return
 *	\n		Match newline
 *	+		Match one or more times (greedy)
 *	+?		Match one or more times (non-greedy)
 *	*		Match zero or more times (greedy)
 *	*?		Match zero or more times (non-greedy)
 *	?		Match zero or once
 *	\xDD		Match byte with hex value 0xDD
 *	\meta		Match one of the meta character: ^$().[*+?\
 */

#ifndef SLRE_HEADER_DEFINED
#define	SLRE_HEADER_DEFINED

/*
 * Compiled regular expression
 */
struct slre {
	unsigned char	code[256];
	unsigned char	data[256];
	unsigned char	code_size;
	unsigned char	data_size;
	unsigned char	num_caps;	/* Number of bracket pairs	*/
	unsigned char	anchored;	/* Must match from string start	*/
	const char	*err_str;	/* Error string			*/
};

/*
 * Captured substring
 */
struct cap {
	const char	*ptr;		/* Pointer to the substring	*/
	int		len;		/* Substring length		*/
};

/*
 * Compile regular expression. If success, 1 is returned.
 * If error, 0 is returned and slre.err_str points to the error message. 
 */
int slre_compile(struct slre *, const char *re);

/*
 * Return 1 if match, 0 if no match. 
 * If `captured_substrings' array is not NULL, then it is filled with the
 * values of captured substrings. captured_substrings[0] element is always
 * a full matched substring. The round bracket captures start from
 * captured_substrings[1].
 * It is assumed that the size of captured_substrings array is enough to
 * hold all captures. The caller function must make sure it is! So, the
 * array_size = number_of_round_bracket_pairs + 1
 */
int slre_match(const struct slre *, const char *buf, int buf_len,
	struct cap *captured_substrings);

#endif /* SLRE_HEADER_DEFINED */
abcm2ps-8.5.2/subs.c0000644000175000017500000011430612461144127012276 0ustar  jefjef/*
 * Low-level utilities.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2015 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.
 */

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#ifdef HAVE_PANGO
#include <pango/pangocairo.h>
#include <pango/pangofc-font.h>
#endif

#include "abc2ps.h" 

char tex_buf[TEX_BUF_SZ];	/* result of tex_str() */
int outft = -1;			/* last font in the output file */

static char *strop;		/* current string output operation */
static float strlw;		/* line width */
static int curft;		/* current (wanted) font */
static int defft;		/* default font */
static char strtx;		/* PostScript text outputing (bits) */
#define TX_STR 1			/* string started */
#define TX_ARR 2			/* glyph/string array started */
#define TX_EXT 4			/* glyph/string array needed */

/* width of characters according to the encoding */
/* these are the widths for Times-Roman, extracted from the 'a2ps' package */
/*fixme-hack: set 500 to control characters for utf-8*/
static short cw_tb[] = {
	500,500,500,500,500,500,500,500,	// 00
	500,500,500,500,500,500,500,500,
	500,500,500,500,500,500,500,500,	// 10
	500,500,500,500,500,500,500,500,
	250,333,408,500,500,833,778,333,	// 20
	333,333,500,564,250,564,250,278,
	500,500,500,500,500,500,500,500,	// 30
	500,500,278,278,564,564,564,444,
	921,722,667,667,722,611,556,722,	// 40
	722,333,389,722,611,889,722,722,
	556,722,667,556,611,722,722,944,	// 50
	722,722,611,333,278,333,469,500,
	333,444,500,444,500,444,333,500,	// 60
	500,278,278,500,278,778,500,500,
	500,500,333,389,278,500,500,722,	// 70
	500,500,444,480,200,480,541,500,
};

static struct u_ps {
	struct u_ps *next;
	char text[2];
} *user_ps;

/* -- print message for internal error and maybe stop -- */
void bug(char *msg, int fatal)
{
	error(1, NULL, "Internal error: %s.", msg);
	if (fatal) {
		fprintf(stderr, "Emergency stop.\n\n");
		exit(EXIT_FAILURE);
	}
	fprintf(stderr, "Trying to continue...\n");
}

/* -- print an error message -- */
void error(int sev,	/* 0: warning, 1: error */
	   struct SYMBOL *s,
	   char *fmt, ...)
{
	va_list args;

	if (s) {
		if (s->as.fn)
			fprintf(stderr, "%s:%d:%d: ", s->as.fn,
					s->as.linenum, s->as.colnum);
		s->as.flags |= ABC_F_ERROR;
	}
	fprintf(stderr, sev == 0 ? "warning: " : "error: ");
	va_start(args, fmt);
	vfprintf(stderr, fmt, args);
	va_end(args);
	fprintf(stderr, "\n");
	if (sev > severity)
		severity = sev;
}

/* -- read a number with a unit -- */
float scan_u(char *str)
{
	float a;
	int nch;

	if (sscanf(str, "%f%n", &a, &nch) == 1) {
		if (str[nch] == '\0' || str[nch] == ' ')
			return a PT;
		if (!strncasecmp(str + nch, "cm", 2))
			return a CM;
		if (!strncasecmp(str + nch, "in", 2))
			return a IN;
		if (!strncasecmp(str + nch, "pt", 2))
			return a PT;
	}
	error(1, NULL, "Unknown unit value \"%s\"", str);
	return 20 PT;
}

/* -- capitalize a string -- */
static void cap_str(char *p)
{
	while (*p != '\0') {
#if 1
/* pb with toupper - works with ASCII and some latin characters only */
		unsigned char c;

		c = (unsigned char) *p;
		if (c >= 'a' && c <= 'z') {
			*p = c & ~0x20;
		} else if (c == 0xc3) {
			p++;
			c = *p;
			if (c >= 0xa0 && c <= 0xbe && c != 0xb7)
				*p = c & ~0x20;
		} else if (c == 0xc4) {
			p++;
			c = *p;
			if (c >= 0x81 && c <= 0xb7 && (c & 0x01))
				(*p)--;
		}
#else
		*p = toupper((unsigned char) *p);
#endif
		p++;
	}
}

/* -- return the character width -- */
float cwid(unsigned char c)
{
	if (c > 0x80) {
		if (c < 0xc0)
			return 0;	// not start of utf8 character
		c = 'a';
	}
	return (float) cw_tb[c] / 1000.;
}

/* -- change string taking care of some tex-style codes -- */
/* Return an estimated width of the string. */
float tex_str(char *s)
{
	char *d;
	unsigned char c1;
	unsigned maxlen, i;
	float w, swfac;

	w = 0;
	d = tex_buf;
	maxlen = sizeof tex_buf - 1;		/* have room for EOS */
	if ((i = curft) <= 0)
		i = defft;
	swfac = cfmt.font_tb[i].swfac;
	while (1) {
		c1 = (unsigned char) *s++;
		if (c1 == '\0')
			break;
		switch (c1) {
		case '\\':
			c1 = *s++;
			if (c1 == '\0') {
				*d = '\0';
				return w;
			}
			switch (c1) {
			case 'n':
				c1 = '\n';
				break;
			case 't':
				c1 = '\t';
				break;
			}
			break;
		case '$':
			if (isdigit((unsigned char) *s)
			 && (unsigned) (*s - '0') < FONT_UMAX) {
				i = *s - '0';
				if (i == 0)
					i = defft;
				swfac = cfmt.font_tb[i].swfac;
				if (--maxlen <= 0)
					break;
				*d++ = c1;
				c1 = *s++;
				goto addchar_nowidth;
			}
			if (*s == '$') {
				if (--maxlen <= 0)
					break;
				*d++ = c1;
				s++;
			}
			break;
		case '&':
			if (*s == '#' && !svg && epsf <= 1) {	/* XML char ref */
				int j;
				long v;

				if (s[1] == 'x')
					i = sscanf(s, "#x%lx;%n", &v, &j);
				else
					i = sscanf(s, "#%ld;%n", &v, &j);
				if (i != 1) {
					error(0, NULL, "Bad XML char reference");
					break;
				}
				if (v < 0x80) {	/* convert to UTF-8 */
					*d++ = v;
				} else if (v < 0x800) {
					*d++ = 0xc0 | (v >> 6);
					*d++ = 0x80 | (v & 0x3f);
				} else if (v < 0x10000) {
					*d++ = 0xe0 | (v >> 12);
					*d++ = 0x80 | ((v >> 6) & 0x3f);
					*d++ = 0x80 | (v & 0x3f);
				} else {
					*d++ = 0xf0 | (v >> 18);
					*d++ = 0x80 | ((v >> 12) & 0x3f);
					*d++ = 0x80 | ((v >> 6) & 0x3f);
					*d++ = 0x80 | (v & 0x3f);
				}
				w += cwid('a') * swfac;
				s += j;
				continue;
			}
			break;
		}
		if (c1 >= 0x80) {
			if (c1 >= 0xc0)
				w += cwid('a') * swfac;	// start of unicode char
		} else if (c1 <= 5) {		/* accidentals from gchord */
			if (--maxlen < 4)
				break;
			switch (c1) {
			case 1:
				*d++ = 0xe2;
				*d++ = 0x99;
				*d++ = 0xaf;
				break;
			case 2:
				*d++ = 0xe2;
				*d++ = 0x99;
				*d++ = 0xad;
				break;
			case 3:
				*d++ = 0xe2;
				*d++ = 0x99;
				*d++ = 0xae;
				break;
			case 4:
				*d++ = 0xf0;
				*d++ = 0x9d;
				*d++ = 0x84;
				*d++ = 0xaa;
				break;
			case 5:
				*d++ = 0xf0;
				*d++ = 0x9d;
				*d++ = 0x84;
				*d++ = 0xab;
				break;
			}
			w += cwid('a') * swfac;
			continue;
		} else {
			w += cwid(c1) * swfac;
		}
	addchar_nowidth:
		if (--maxlen <= 0)
			break;
		*d++ = c1;
	}
	*d = '\0';
	if (maxlen <= 0)
		error(0, NULL, "Text too large - ignored part: '%s'", s);
	return w;
}

#ifdef HAVE_PANGO
#define PG_SCALE (PANGO_SCALE * 72 / 96)	/* 96 DPI */

static PangoFontDescription *desc_tb[MAXFONTS];
static PangoLayout *layout = (PangoLayout *) -1;
static PangoAttrList *attrs;
static int out_pg_ft = -1;		/* current pango font */
static GString *pg_str;

/* -- initialize the pango mechanism -- */
void pg_init(void)
{
	static PangoContext *context;

	context = pango_font_map_create_context(
			pango_cairo_font_map_get_default());
	if (context)
		layout = pango_layout_new(context);
	if (!layout) {
		error(0, NULL, "pango disabled\n");
		cfmt.pango = 0;
	} else {
		pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
//		pango_layout_set_spacing(layout, 0);
		pg_str = g_string_sized_new(256);
	}
}
void pg_reset_font(void)
{
	out_pg_ft = -1;
}

static void desc_font(int fnum)
{
	char font_name[128], *p;

	if (desc_tb[fnum] == 0) {
		p = font_name;
		sprintf(p, "%s 10", fontnames[fnum]);
		while (*p != '\0') {
			if (*p == '-')
				*p = ' ';
			p++;
		}
		desc_tb[fnum] = pango_font_description_from_string(font_name);
	}
}

/* output a line */
static void pg_line_output(PangoLayoutLine *line)
{
	GSList *runs_list;
	PangoGlyphInfo *glyph_info;
	char tmp[256];
	const char *fontname = NULL;
	int ret, glypharray;

	outft = -1;
	glypharray = 0;
	for (runs_list = line->runs; runs_list; runs_list = runs_list->next) {
		PangoLayoutRun *run = runs_list->data;
		PangoItem *item = run->item;
		PangoGlyphString *glyphs = run->glyphs;
		PangoAnalysis *analysis = &item->analysis;
		PangoFont *font = analysis->font;
		PangoFcFont *fc_font = PANGO_FC_FONT(font);
		FT_Face face = pango_fc_font_lock_face(fc_font);
		PangoFontDescription *ftdesc =
				pango_font_describe(font);
		int wi = pango_font_description_get_size(ftdesc);
		int i, c;

		if (pango_font_description_get_size(ftdesc) != wi) {
			wi = pango_font_description_get_size(ftdesc);
			fontname = NULL;
		}
		for (i = 0; i < glyphs->num_glyphs; i++) {
			glyph_info = &glyphs->glyphs[i];
			c = glyph_info->glyph;
			if (c == PANGO_GLYPH_EMPTY)
				continue;
			if (c & PANGO_GLYPH_UNKNOWN_FLAG) {
				c &= ~PANGO_GLYPH_UNKNOWN_FLAG;
				error(0, NULL, "char %04x not treated\n", c);
				continue;
			}

			ret = FT_Load_Glyph(face,
					c,		// PangoGlyph = index
					FT_LOAD_NO_SCALE);
			if (ret != 0) {
				error(0, NULL, "freetype error %d\n", ret);
			} else if (FT_HAS_GLYPH_NAMES(face)) {
				if (FT_Get_Postscript_Name(face) != fontname) {
					fontname = FT_Get_Postscript_Name(face);
					if (glypharray)
						a2b("]glypharray");
						a2b("\n");
					a2b("/%s %.1f selectfont[",
						fontname,
						(float) wi / PG_SCALE);
					glypharray = 1;
				}
				FT_Get_Glyph_Name((FT_FaceRec *) face, c,
						tmp, sizeof tmp);
				a2b("/%s", tmp);
			} else {
				a2b("%% glyph: %s %d\n",
					FT_Get_Postscript_Name(face), c);
			}
		}
		pango_fc_font_unlock_face(fc_font);
	}
	if (glypharray)
		a2b("]glypharray");
}

static void str_font_change(int start,
			int end)
{
	struct FONTSPEC *f;
	int fnum;
	PangoAttribute *attr1, *attr2;

	f = &cfmt.font_tb[curft];
	fnum = f->fnum;
	if (f->size == 0) {
		error(0, NULL, "Font \"%s\" with a null size - set to 8",
			fontnames[fnum]);
		f->size = 8;
	}
	desc_font(fnum);
	
	attr1 = pango_attr_font_desc_new(desc_tb[fnum]);
	attr1->start_index = start;
	attr1->end_index = end;
	pango_attr_list_insert(attrs, attr1);
	attr2 = pango_attr_size_new((int) (f->size * PG_SCALE));
	attr2->start_index = start;
	attr2->end_index = end;
	pango_attr_list_insert(attrs, attr2);
}

static void str_set_font(char *p)
{
	GString *str;
	char *q;
	int start;

	str = pg_str;
	start = str->len;
	q = p;
	while (*p != '\0') {
		switch (*p) {
		case '$':
			if (isdigit((unsigned char) p[1])
			 && (unsigned) (p[1] - '0') < FONT_UMAX) {
				if (p > q)
					str = g_string_append_len(str, q, p - q);
				if (curft != p[1] - '0') {
					str_font_change(start, str->len);
					start = str->len;
					curft = p[1] - '0';
					if (curft == 0)
						curft = defft;
				}
				p += 2;
				q = p;
				continue;
			}
			if (p[1] == '$') {
				str = g_string_append_len(str, q, p - q);
				q = ++p;
			}
			break;
		}
		p++;
	}
	if (p > q) {
		str = g_string_append_len(str, q, p - q);
		str_font_change(start, str->len);
	}
	pg_str = str;
}

/* -- output a string using the pango and freetype libraries -- */
static void str_pg_out(char *p, int action)
{
	PangoLayoutLine *line;
	int wi;
	float w;

//fixme: test
//a2b("\n%% t: '%s'\n", p);
	if (out_pg_ft != curft)
		out_pg_ft = -1;

	/* guitar chord with TABs */
	if (action == A_GCHEXP) {
		char *q;

		/* get the inter TAB width (see draw_gchord) */
		q = mbf - 1;
		while (q[-1] != ' ')
			q--;
		mbf = q;
		w = atof(q);
		for (;;) {
			q = strchr(p, '\t');
			if (!q)
				break;
			*q = '\0';
			str_pg_out(p, A_LEFT);
			a2b(" %.1f 0 RM ", w);
			p = q + 1;
		}
	}

	attrs = pango_attr_list_new();
	str_set_font(p);

	pango_layout_set_text(layout, pg_str->str, pg_str->len);
	pango_layout_set_attributes(layout, attrs);

	/* only one line */
	line = pango_layout_get_line_readonly(layout, 0);
	switch (action) {
	case A_CENTER:
	case A_RIGHT:
		pango_layout_get_size(layout, &wi, NULL);
		if (action == A_CENTER)
			wi /= 2;
//		w = (float) wi / PG_SCALE;
		w = (float) wi / PANGO_SCALE;
		a2b("-%.1f 0 RM ", w);
		break;
	}
	pg_line_output(line);
	pango_layout_set_attributes(layout, NULL);
	pg_str = g_string_truncate(pg_str, 0);
	pango_attr_list_unref(attrs);
}

/* output a justified or filled paragraph */
static void pg_para_output(int job)
{
	GSList *lines, *runs_list;
	PangoLayoutLine *line;
	PangoGlyphInfo *glyph_info;
	char tmp[256];
	const char *fontname = NULL;
	int ret, glypharray;
	int wi;
	float y;

	pango_layout_set_text(layout, pg_str->str,
			pg_str->len - 1);	/* remove the last space */
	pango_layout_set_attributes(layout, attrs);
	outft = -1;
	glypharray = 0;
	wi = 0;
	y = 0;
	lines = pango_layout_get_lines_readonly(layout);

	for (; lines; lines = lines->next) {
		PangoRectangle pos;

		line = lines->data;
		pango_layout_line_get_extents(line, NULL, &pos);
		y += (float) pos.height
				* .87		/* magic! */
				/ PANGO_SCALE;

		for (runs_list = line->runs; runs_list; runs_list = runs_list->next) {
			PangoLayoutRun *run = runs_list->data;
			PangoItem *item = run->item;
			PangoGlyphString *glyphs = run->glyphs;
			PangoAnalysis *analysis = &item->analysis;
			PangoFont *font = analysis->font;
			PangoFcFont *fc_font = PANGO_FC_FONT(font);
			FT_Face face = pango_fc_font_lock_face(fc_font);
			PangoFontDescription *ftdesc =
					pango_font_describe(font);
			int i, g, set_move, x;

			if (pango_font_description_get_size(ftdesc) != wi) {
				wi = pango_font_description_get_size(ftdesc);
				fontname = NULL;
			}
//printf("font size: %.2f\n", (float) wi / PG_SCALE);

			pango_layout_index_to_pos(layout, item->offset, &pos);
			x = pos.x;
			set_move = 1;
			for (i = 0; i < glyphs->num_glyphs; i++) {
				glyph_info = &glyphs->glyphs[i];
				g = glyph_info->glyph;
				if (g == PANGO_GLYPH_EMPTY)
					continue;
				if (set_move) {
					set_move = 0;
					if (glypharray) {
						a2b("]glypharray");
						glypharray = 0;
					}
					a2b("\n");
					a2b("%.2f %.2f M ",
						(float) x / PANGO_SCALE, -y);
				}
				x += glyph_info->geometry.width;
				if (g & PANGO_GLYPH_UNKNOWN_FLAG) {
					g &= ~PANGO_GLYPH_UNKNOWN_FLAG;
					error(0, NULL, "char %04x not treated\n", g);
					continue;
				}

				ret = FT_Load_Glyph(face,
						g,		// PangoGlyph = index
						FT_LOAD_NO_SCALE);
				if (ret != 0) {
					fprintf(stdout, "%%%% freetype error %d\n", ret);
				} else if (FT_HAS_GLYPH_NAMES(face)) {
					if (FT_Get_Postscript_Name(face) != fontname) {
						fontname = FT_Get_Postscript_Name(face);
						if (glypharray)
							a2b("]glypharray");
						a2b("\n/%s %.1f selectfont[",
							fontname,
							(float) wi / PG_SCALE);
						glypharray = 1;
					}
					FT_Get_Glyph_Name((FT_FaceRec *) face, g,
							tmp, sizeof tmp);
					if (job == T_JUSTIFY
					 && strcmp(tmp, "space") == 0) {
						set_move = 1;
						continue;
					}
					if (!glypharray) {
						a2b("[");
						glypharray = 1;
					}
					a2b("/%s", tmp);
				} else {
					a2b("%% glyph: %s %d\n",
						FT_Get_Postscript_Name(face), g);
				}
			}
			pango_fc_font_unlock_face(fc_font);
			if (glypharray) {
				a2b("]glypharray\n");
				glypharray = 0;
			}
		}
		if (glypharray) {
			a2b("]glypharray\n");
			glypharray = 0;
		}
	}
	bskip(y);
	pango_layout_set_attributes(layout, NULL);
	pg_str = g_string_truncate(pg_str, 0);
}

/* output of filled / justified text */
static void pg_write_text(char *s, int job, float parskip)
{
	char *p;

	curft = defft;
	pango_layout_set_width(layout, strlw * PANGO_SCALE);
	pango_layout_set_justify(layout, job == T_JUSTIFY);
	attrs = pango_attr_list_new();

	p = s;
	while (*p != '\0') {
		if (*p++ != '\n')
			continue;
		if (*p == '\n') {		/* if empty line */
			p[-1] = '\0';
			tex_str(s);
			str_set_font(tex_buf);
			if (pg_str->len > 0)
				pg_para_output(job);
			bskip(parskip);
			buffer_eob();
			s = ++p;
			continue;
		}
//fixme: maybe not useful
		p [-1] = ' ';
	}
	tex_str(s);
	str_set_font(tex_buf);
	if (pg_str->len)
		pg_para_output(job);
	pango_attr_list_unref(attrs);
}

/* check if pango is needed */
static int is_latin(unsigned char *p)
{
	while (*p != '\0') {
		if (*p >= 0xc6) {
			if (*p == 0xe2) {
				if (p[1] != 0x99
				 || p[2] < 0xad || p[2] > 0xaf)
					return 0;
				p += 2;
			} else if (*p == 0xf0) {
				if (p[1] != 0x9d
				 || p[2] != 0x84
				 || p[3] < 0xaa || p[3] > 0xab)
					return 0;
			} else {
				return 0;
			}
		}
		p++;
	}
	return 1;
}
#endif /* HAVE_PANGO */

/* -- set the default font of a string -- */
void str_font(int ft)
{
	curft = defft = ft;
}

/* -- get the current and default fonts -- */
void get_str_font(int *cft, int *dft)
{
	*cft = curft;
	*dft = defft;
}

/* -- set the current and default fonts -- */
void set_str_font(int cft, int dft)
{
	curft = cft;
	defft = dft;
}

/* close a string */
static void str_end(int end)
{
	if (strtx & TX_STR) {
		a2b(")");
		strtx &= ~TX_STR;
		if (!(strtx & TX_ARR))
			a2b("%s", strop);
	}
	if (!end || !(strtx & TX_ARR))
		return;
	strtx &= ~TX_ARR;
	a2b("]arrayshow");
}

/* check if some non ASCII characters */
static int non_ascii_p(char *p)
{
	while (*p != '\0') {
		if ((signed char) *p++ < 0)
			return 1;
	}
	return 0;
}

/* -- output one string -- */
static void str_ft_out1(char *p, int l)
{
	if (curft != outft) {
		str_end(1);
		a2b(" ");
		set_font(curft);
	}
	if (!(strtx & TX_STR)) {
		a2b("(");
		strtx |= TX_STR;
	}
	a2b("%.*s", l, p);
}

static char *strop_tb[] = {	/* index = action (A_xxxx) */
	"show",
	"showc",
	"showr",
	"lyshow",
	"gcshow",
	"anshow",
	"gxshow",
};

/* -- output a string and the font changes -- */
static void str_ft_out(char *p, int end)
{
	int use_glyph;
	char *q;

	use_glyph = !svg && epsf <= 1 &&	/* not SVG */
		get_font_encoding(curft) == 0;	/* utf-8 font */
	if (use_glyph && non_ascii_p(p)) {
		if (curft != outft) {
			str_end(1);
			a2b(" ");
			set_font(curft);
		}
		str_end(0);
		if (!(strtx & TX_ARR)) {
			a2b("[");
			strtx |= TX_ARR;
		}
	}
	q = p;
	while (*p != '\0') {
		if ((unsigned char) *p >= 0xc2
		 && use_glyph) {
			if (p > q) {
				str_ft_out1(q, p - q);
//			} else if (curft != outft) {
//				str_end(1);
//				a2b(" ");
//				set_font(curft);
			}
			str_end(0);
			if (curft != outft) {
				str_end(1);
				a2b(" ");
				set_font(curft);
			}
			if (!(strtx & TX_ARR)) {
				a2b("[");
				strtx |= TX_ARR;
			}
			q = p = glyph_out(p);
			continue;
		}
		switch ((unsigned char) *p) {
		case '$':
			if (isdigit((unsigned char) p[1])
			 && (unsigned) (p[1] - '0') < FONT_UMAX) {
				if (p > q)
					str_ft_out1(q, p - q);
				if (curft != p[1] - '0') {
					curft = p[1] - '0';
					if (curft == 0)
						curft = defft;
					use_glyph = !svg && epsf <= 1 &&
						 get_font_encoding(curft) == 0;
				}
				p += 2;
				q = p;
				continue;
			}
			if (p[1] == '$') {
				str_ft_out1(q, p - q);
				q = ++p;
			}
			break;
		case '(':
		case ')':
		case '\\':
			if (p > q)
				str_ft_out1(q, p - q);
			str_ft_out1("\\", 1);
			q = p;
			break;
		}
		p++;
	}
	if (p > q)
		str_ft_out1(q, p - q);
	if (end && strtx)
		str_end(1);
}

/* -- output a string, handling the font changes -- */
void str_out(char *p, int action)
{
	if (curft <= 0)		/* first call */
		curft = defft;

	/* special case when font change at start of text */
	if (*p == '$' && isdigit((unsigned char) p[1])
	 && (unsigned) (p[1] - '0') < FONT_UMAX) {
		if (curft != p[1] - '0') {
			curft = p[1] - '0';
			if (curft == 0)
				curft = defft;
		}
		p += 2;
	}

#ifdef HAVE_PANGO
//fixme: pango KO if user modification of ly/gc/an/gxshow
	/* use pango if some characters are out of the utf-array (in syms.c) */
	if (cfmt.pango) {
		if (cfmt.pango == 2 || !is_latin((unsigned char *) p)) {
			str_pg_out(p, action);	/* output the string */
			return;
		}
	}
#endif

	/* direct output if no font change
	 * nor non ASCII characters */
	if (!strchr(p, '$')
	 && !non_ascii_p(p)) {
		strop = strop_tb[action];
		str_ft_out(p, 1);		/* output the string */
		return;
	}

	/* if not left aligned, build a PS function */
	switch (action) {
	case A_CENTER:
	case A_RIGHT:
		if (!svg && epsf <= 1) {
			a2b("/str{");
			outft = -1;
			strop = "strop";
			break;
		}
		/* fall thru */
	default:
		strop = strop_tb[action];
		break;
	}

	str_ft_out(p, 1);		/* output the string */

	/* if not left aligned, call the PS function */
	if (svg || epsf > 1)		/* not for SVG */
		return;
	if (action == A_CENTER || action == A_RIGHT) {
		a2b("}def\n"
			"strw w");
		if (action == A_CENTER)
			a2b(" 0.5 mul");
		a2b(" neg 0 RM str");
	}
}

/* -- output a string with TeX translation -- */
void put_str(char *str, int action)
{
	tex_str(str);
	str_out(tex_buf, action);
	a2b("\n");
}

/* -- output a header information -- */
static void put_inf(struct SYMBOL *s)
{
	char *p;

	p = s->as.text;
	if (p[1] == ':')
		p += 2;
	while (isspace((unsigned char) *p))
		p++;
	put_str(p, A_LEFT);
}

/* -- output a header format '111 (222)' -- */
static void put_inf2r(struct SYMBOL *s1,
			struct SYMBOL *s2,
			int action)
{
	char buf[256], *p, *q;

	if (!s1) {
		s1 = s2;
		s2 = NULL;
	}
	p = &s1->as.text[2];
	if (s1->as.text[0] == 'T')
		p = trim_title(p, s1);
	if (s2) {
		buf[sizeof buf - 1] = '\0';
		strncpy(buf, p, sizeof buf - 1);
		q = buf + strlen(buf);
		if (q < buf + sizeof buf - 4) {
			*q++ = ' ';
			*q++ = '(';
			p = &s2->as.text[2];
			strncpy(q, p, buf + sizeof buf - 2 - q);
			q += strlen(q);
			*q++ = ')';
			*q = '\0';
		}
		p = buf;
	}
	put_str(p, action);
}

/* -- write a text block (%%begintext / %%text / %%center) -- */
void write_text(char *cmd, char *s, int job)
{
	int nw;
#ifdef HAVE_PANGO
	int do_pango;
#endif
	float lineskip, parskip, strw;
	char *p;
	struct FONTSPEC *f;

	str_font(TEXTFONT);
	strlw = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
		- cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale;

	f = &cfmt.font_tb[TEXTFONT];
	lineskip = f->size * cfmt.lineskipfac;
	parskip = f->size * cfmt.parskipfac;

	/* follow lines */
	switch (job) {
	case T_LEFT:
	case T_CENTER:
	case T_RIGHT:
		switch (job) {
		case T_LEFT:
#if T_LEFT != A_LEFT
			job = A_LEFT;
#endif
			strlw = 0;
			break;
		case T_CENTER:
#if T_CENTER != A_CENTER
			job = A_CENTER;
#endif
			strlw /= 2;
			break;
		default:
#if T_RIGHT != A_RIGHT
			job = A_RIGHT;
#endif
			break;
		}
		p = s;
		while (*s != '\0') {
			while (*p != '\0' && *p != '\n')
				p++;
			if (*p != '\0')
				*p++ = '\0';
			if (*s == '\0') {		// new paragraph
				bskip(parskip);
				buffer_eob();
				while (*p == '\n') {
					bskip(lineskip);
					p++;
				}
				if (*p == '\0')
					goto skip;
			} else {
				bskip(lineskip);
				a2b("%.1f 0 M", strlw);
				put_str(s, job);
			}
			s = p;
		}
		goto skip;
	}

	/* fill or justify lines */
#ifdef HAVE_PANGO
	do_pango = cfmt.pango;
	if (do_pango == 1)
		do_pango = !is_latin((unsigned char *) s);
	if (do_pango) {
		pg_write_text(s, job, parskip);
		goto skip;
	}
#endif
//	curft = defft;
	nw = 0;					/* number of words */
	strw = 0;				/* have gcc happy */
	strop = job == T_FILL ? "show" : "strop";
	while (*s != '\0') {
		float lw;

		if (*s == '\n') {		/* empty line = new paragraph */
			if (strtx) {
				str_end(1);
				if (job == T_JUSTIFY)
					a2b("}def\n"
					    "/strop/show load def str");
				a2b("\n");
			}
//			a2b("\n");
			bskip(parskip);
			buffer_eob();
//			while (isspace((unsigned char) *s))
//				s++;
			while (*s == '\n') {
				bskip(lineskip);
				s++;
			}
			if (*s == '\0')
				goto skip;
			nw = 0;
//			a2b("0 0 M");
//			if (job != T_FILL) {
//				a2b("/str{");
//				outft = -1;
//			}
//			continue;
		}

		if (nw == 0) {			/* if new paragraph */
			bskip(lineskip);
			a2b("0 0 M");
			if (job != T_FILL) {
				a2b("/str{");
				outft = -1;
			}
			strw = 0;		/* current line width */
		}

		/* get a word */
		p = s;
		while (*p != '\0' && !isspace((unsigned char) *p))
			p++;
		if (*p != '\0') {
			char *q;

			q = p;
			if (*p != '\n') {
				do {
					p++;
				} while (*p != '\n' && isspace((unsigned char) *p));
			}
			if (*p == '\n')
				p++;
			*q = '\0';
		}

		lw = tex_str(s);
		if (strw + lw > strlw) {
			str_end(1);
			if (job == T_JUSTIFY) {
				int n;

				n = nw - 1;
				if (n <= 0)
					n = 1;
				if (svg || epsf > 1)
					a2b("}def\n"
						"%.1f jshow"
						"/strop/show load def str",
						strlw);
				else
					a2b("}def\n"
						"strw"
						"/w %.1f w sub %d div def"
						"/strop/jshow load def str",
						strlw, n);
			}
			a2b("\n");
			bskip(lineskip);
			a2b("0 0 M");
			if (job == T_JUSTIFY) {
				a2b("/str{");
				outft = -1;
			}
			nw = 0;
			strw = 0;
		}

		if (nw != 0) {
			str_ft_out1(" ", 1);
			strw += cwid(' ') * cfmt.font_tb[curft].swfac;
		}
		str_ft_out(tex_buf, 0);
		strw += lw;
		nw++;

		s = p;
	}
	if (strtx) {
		str_end(1);
		if (job == T_JUSTIFY)
			a2b("}def\n"
				"/strop/show load def str");
	}
//	if (mbf[-1] != '\n')
		a2b("\n");
skip:
	bskip(parskip);
	buffer_eob();
}

/* -- output a line of words after tune -- */
static int put_wline(char *p,
			float x,
			int right)
{
	char *q, *r, sep;

	while (isspace((unsigned char) *p))
		p++;
	if (*p == '$' && isdigit((unsigned char) p[1])
	 && (unsigned) (p[1] - '0') < FONT_UMAX) {
		if (curft != p[1] - '0') {
			curft = p[1] - '0';
			if (curft == 0)
				curft = defft;
		}
		p += 2;
	}
	r = 0;
	q = p;
	if (isdigit((unsigned char) *p) || p[1] == '.') {
		while (*p != '\0') {
			p++;
			if (*p == ' '
			 || p[-1] == ':'
			 || p[-1] == '.')
				break;
		}
		r = p;
		while (*p == ' ')
			p++;
	}

	if (r != 0) {
		sep = *r;
		*r = '\0';
		a2b("%.1f 0 M", x);
		put_str(q, A_RIGHT);
		*r = sep;
	}
	if (*p != '\0') {
		a2b("%.1f 0 M", x + 5);
		put_str(p, A_LEFT);
	}
	return *p == '\0' && r == 0;
}

/* -- output the words after tune -- */
void put_words(struct SYMBOL *words)
{
	struct SYMBOL *s, *s_end, *s2;
	char *p;
	int i, n, have_text, max2col;
	float middle;

	buffer_eob();
	str_font(WORDSFONT);

	/* see if we may have 2 columns */
	middle = 0.5 * ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
		- cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale;
	max2col = (int) ((middle - 45.) / (cwid('a') * cfmt.font_tb[WORDSFONT].swfac));
	n = 0;
	have_text = 0;
	for (s = words; s != 0; s = s->next) {
		p = &s->as.text[2];
/*fixme:utf8*/
		if ((int) strlen(p) > max2col) {
			n = 0;
			break;
		}
		if (*p == '\0') {
			if (have_text) {
				n++;
				have_text = 0;
			}
		} else {
			have_text = 1;
		}
	}
	if (n > 0) {
		n++;
		n /= 2;
		i = n;
		have_text = 0;
		s_end = words;
		for (;;) {
			p = &s_end->as.text[2];
			while (isspace((unsigned char) *p))
				p++;
			if (*p == '\0') {
				if (have_text && --i <= 0)
					break;
				have_text = 0;
			} else {
				have_text = 1;
			}
			s_end = s_end->next;
		}
		s2 = s_end->next;
	} else {
		s_end = NULL;
		s2 = NULL;
	}

	/* output the text */
	bskip(cfmt.wordsspace);
	for (s = words; s || s2; ) {
//fixme:should also permit page break on stanza start
		if (s && s->as.text[2] == '\0')
			buffer_eob();
		bskip(cfmt.lineskipfac * cfmt.font_tb[WORDSFONT].size);
		if (s) {
			put_wline(&s->as.text[2], 45., 0);
			s = s->next;
			if (s == s_end)
				s = NULL;
		}
		if (s2) {
			if (put_wline(&s2->as.text[2], 20. + middle, 1)) {
				if (--n == 0) {
					if (s) {
						n++;
					} else if (s2->next) {

						/* center the last words */
/*fixme: should compute the width average.. */
						middle *= 0.6;
					}
				}
			}
			s2 = s2->next;
		}
	}
//	buffer_eob();
}

/* -- output history -- */
void put_history(void)
{
	struct SYMBOL *s, *s2;
	int font;
	unsigned u;
	float w, h;
	char tmp[265];

	font = 0;
	for (s = info['I' - 'A']; s; s = s->next) {
		u = s->as.text[0] - 'A';
		if (!(cfmt.fields[0] & (1 << u))
		 || (s2 = info[u]) == NULL)
			continue;
		if (!font) {
			bskip(cfmt.textspace);
			str_font(HISTORYFONT);
			font = 1;
		}
		get_str(tmp, &s->as.text[1], sizeof tmp);
		w = tex_str(tmp);
		h = cfmt.font_tb[HISTORYFONT].size * cfmt.lineskipfac;
		set_font(HISTORYFONT);
//		a2b("0 0 M(%s)show ", tex_buf);
		a2b("0 0 M");
		str_out(tex_buf, A_LEFT);
		for (;;) {
			put_inf(s2);
			if ((s2 = s2->next) == NULL)
				break;
			bskip(h);
			a2b("%.2f 0 M ", w);
		}
		bskip(h * 1.2);
		buffer_eob();
	}
}

/* -- move trailing "The" to front, set to uppercase letters or add xref -- */
char *trim_title(char *p, struct SYMBOL *title)
{
	char *b, *q, *r;
static char buf[STRL1];

	q = NULL;
	if (cfmt.titletrim) {
		q = strrchr(p, ',');
		if (q) {
			if (q[1] != ' ' || !isupper((unsigned char) q[2])
			 || strlen(q) > 7	/* word no more than 4 characters */
			 || strchr(q + 2, ' '))
				q = NULL;
		}
	}
	if (title != info['T' - 'A']
	 || !(cfmt.fields[0] & (1 << ('X' - 'A'))))
		title = NULL;
	if (!q
	 && !title
	 && !cfmt.titlecaps)
		return p;		/* keep the title as it is */
	b = buf;
	r = &info['X' - 'A']->as.text[2];
	if (title
	 && *r != '\0') {
		if (strlen(p) + strlen(r) + 3 >= sizeof buf) {
			error(1, NULL, "Title or X: too long");
			return p;
		}
		b += sprintf(b, "%s.  ", r);
	} else {
		if (strlen(p) >= sizeof buf) {
			error(1, NULL, "Title too long");
			return p;
		}
	}
	if (q)
		sprintf(b, "%s %.*s", q + 2, (int) (q - p), p);
	else
		strcpy(b, p);
	if (cfmt.titlecaps)
		cap_str(buf);
	return buf;
}

/* -- write a title -- */
void write_title(struct SYMBOL *s)
{
	char *p;
	float sz;

	p = &s->as.text[2];
	if (*p == '\0')
		return;
	if (s == info['T' - 'A']) {
		sz = cfmt.font_tb[TITLEFONT].size;
		bskip(cfmt.titlespace + sz);
		str_font(TITLEFONT);
		a2b("%% --- title");
	} else {
		sz = cfmt.font_tb[SUBTITLEFONT].size;
		bskip(cfmt.subtitlespace + sz);
		str_font(SUBTITLEFONT);
		a2b("%% --- titlesub");
	}
	a2b(" %s\n", p);
	if (cfmt.titleleft)
		a2b("0");
	else
		a2b("%.1f",
		     0.5 * ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
			- cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale);
	a2b(" %.1f M", sz * 0.2);
	p = trim_title(p, s);
	put_str(p, cfmt.titleleft ? A_LEFT : A_CENTER);
}

/* -- write heading with format -- */
static void write_headform(float lwidth)
{
	char *p, *q;
	struct SYMBOL *s;
	struct FONTSPEC *f;
	int align, i;
	unsigned j;
	float x, y, xa[3], ya[3], sz, yb[3];	/* !! see action A_xxx */
	char inf_nb[26];
	INFO inf_s;
	char inf_ft[26];
	float inf_sz[26];
	char fmt[64];

	memset(inf_nb, 0, sizeof inf_nb);
	memset(inf_ft, HISTORYFONT, sizeof inf_ft);
	inf_ft['A' - 'A'] = INFOFONT;
	inf_ft['C' - 'A'] = COMPOSERFONT;
	inf_ft['O' - 'A'] = COMPOSERFONT;
	inf_ft['P' - 'A'] = PARTSFONT;
	inf_ft['Q' - 'A'] = TEMPOFONT;
	inf_ft['R' - 'A'] = INFOFONT;
	inf_ft['T' - 'A'] = TITLEFONT;
	inf_ft['X' - 'A'] = TITLEFONT;
	memcpy(inf_s, info, sizeof inf_s);
	memset(inf_sz, 0, sizeof inf_sz);
	inf_sz['A' - 'A'] = cfmt.infospace;
	inf_sz['C' - 'A'] = cfmt.composerspace;
	inf_sz['O' - 'A'] = cfmt.composerspace;
	inf_sz['R' - 'A'] = cfmt.infospace;
	p = cfmt.titleformat;
	j = 0;
	for (;;) {
		while (isspace((unsigned char) *p))
			p++;
		if (*p == '\0')
			break;
		i = *p - 'A';
		if ((unsigned) i < 26) {
			inf_nb[i]++;
			switch (p[1]) {
			default:
				align = A_CENTER;
				break;
			case '1':
				align = A_RIGHT;
				p++;
				break;
			case '-':
				align = A_LEFT;
				p++;
				break;
			}
			if (j < sizeof fmt - 4) {
				fmt[j++] = i;
				fmt[j++] = align;
			}
		} else if (*p == ',') {
			if (j < sizeof fmt - 3)
				fmt[j++] = 126;		/* next line */
		} else if (*p == '+') {
			if (j > 0 && fmt[j - 1] < 125
			 && j < sizeof fmt - 4) {
				fmt[j++] = 125;		/* concatenate */
				fmt[j++] = 0;
			}
/*new fixme: add free text "..." ?*/
		}
		p++;
	}
	fmt[j++] = 126;			/* newline */
	fmt[j] = 127;			/* end of format */

	ya[0] = ya[1] = ya[2] = cfmt.titlespace;
	xa[0] = 0;
	xa[1] = lwidth * 0.5;
	xa[2] = lwidth;

	p = fmt;
	for (;;) {
		yb[0] = yb[1] = yb[2] = y = 0;
		q = p;
		for (;;) {
			i = *q++;
			if (i >= 126)		/* if newline */
				break;
			align = *q++;
			if (yb[align] != 0
			 || i == 125)
				continue;
			s = inf_s[i];
			if (s == 0 || inf_nb[i] == 0)
				continue;
			j = inf_ft[i];
			f = &cfmt.font_tb[j];
			sz = f->size * 1.1 + inf_sz[i];
			if (y < sz)
				y = sz;
			yb[align] = sz;
/*fixme:should count the height of the concatenated field*/
		}
		for (i = 0; i < 3; i++)
			ya[i] += y - yb[i];
		for (;;) {
			i = *p++;
			if (i >= 126)		/* if newline */
				break;
			align = *p++;
			if (i == 125)
				continue;
			s = inf_s[i];
			if (!s || inf_nb[i] == 0)
				continue;
			j = inf_ft[i];
			str_font(j);
			x = xa[align];
			f = &cfmt.font_tb[j];
			sz = f->size * 1.1 + inf_sz[i];
			y = ya[align] + sz;
			if (s->as.text[2] != '\0') {
				if (i == 'T' - 'A') {
					if (s == info['T' - 'A'])
						a2b("%% --- title");
					else
						a2b("%% --- titlesub");
					a2b(" %s\n", &s->as.text[2]);
				}
				a2b("%.1f %.1f M ", x, -y);
			}
			if (*p == 125) {	/* concatenate */
			    p += 2;
/*fixme: do it work with different fields*/
			    if (*p == i && p[1] == align
			     && s->next) {
				char buf[256], *r;

				q = s->as.text;
				if (q[1] == ':')
					q += 2;
				while (isspace((unsigned char) *q))
					q++;
				if (i == 'T' - 'A')
					q = trim_title(q, s);
				strncpy(buf, q, sizeof buf - 1);
				buf[sizeof buf - 1] = '\0';
				j = strlen(buf);
				if (j < sizeof buf - 1) {
					buf[j] = ' ';
					buf[j + 1] = '\0';
				}
				s = s->next;
				q = s->as.text;
				if (q[1] == ':')
					q += 2;
				while (isspace((unsigned char) *q))
					q++;
				if (s->as.text[0] == 'T'/* && s->as.text[1] == ':'*/)
					q = trim_title(q, s);
				r = buf + strlen(buf);
				strncpy(r, q, buf + sizeof buf - r - 1);
				tex_str(buf);
				str_out(tex_buf, align);
				a2b("\n");
				inf_nb[i]--;
				p += 2;
			    } else {
				put_inf2r(s, NULL, align);
			    }
			} else if (i == 'Q' - 'A') {	/* special case for tempo */
				if (align != A_LEFT) {
					float w;

					w = -tempo_width(s);
					if (align == A_CENTER)
						w *= 0.5;
					a2b("%.1f 0 RM ", w);
				}
				write_tempo(s, 0, 0.75);
				info['Q' - 'A'] = NULL;	/* don't display in tune */
			} else {
				put_inf2r(s, NULL, align);
			}
			if (inf_s[i] == info['T' - 'A']) {
				inf_ft[i] = SUBTITLEFONT;
				str_font(SUBTITLEFONT);
				f = &cfmt.font_tb[SUBTITLEFONT];
				inf_sz[i] = cfmt.subtitlespace;
				sz = f->size * 1.1 + inf_sz[i];
			}
			s = s->next;
			if (inf_nb[i] == 1) {
				while (s) {
					y += sz;
					a2b("%.1f %.1f M ", x, -y);
					put_inf2r(s, 0, align);
					s = s->next;
				}
			}
			inf_s[i] = s;
			inf_nb[i]--;
			ya[align] = y;
		}
		if (ya[1] > ya[0])
			ya[0] = ya[1];
		if (ya[2] > ya[0])
			ya[0] = ya[2];
		if (*p == 127) {
			bskip(ya[0]);
			break;
		}
		ya[1] = ya[2] = ya[0];
	}
}

/* -- output the tune heading -- */
void write_heading(void)
{
	struct SYMBOL *s, *rhythm, *area, *author, *composer, *origin;
	float lwidth, down1, down2;

	lwidth = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
			- cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale;

	if (cfmt.titleformat && cfmt.titleformat[0] != '\0') {
		write_headform(lwidth);
		bskip(cfmt.musicspace);
		return;
	}

	/* titles */
	if (cfmt.fields[0] & (1 << ('T' - 'A'))) {
		for (s = info['T' - 'A']; s; s = s->next)
			write_title(s);
	}

	/* rhythm, composer, origin */
	down1 = cfmt.composerspace + cfmt.font_tb[COMPOSERFONT].size;
	rhythm = (first_voice->key.mode >= BAGPIPE
			&& !cfmt.infoline
			&& (cfmt.fields[0] & (1 << ('R' - 'A'))))
					? info['R' - 'A'] : NULL;
	if (rhythm) {
		str_font(COMPOSERFONT);
		a2b("0 %.1f M ", -cfmt.composerspace);
		put_inf(rhythm);
		down1 = cfmt.composerspace;
	}
	area = author = NULL;
	if (parse.abc_vers != (2 << 16))
		area = info['A' - 'A'];
	else
		author = info['A' - 'A'];
	composer = (cfmt.fields[0] & (1 << ('C' - 'A'))) ? info['C' - 'A'] : NULL;
	origin = (cfmt.fields[0] & (1 << ('O' - 'A'))) ? info['O' - 'A'] : NULL;
	if (composer || origin || author || cfmt.infoline) {
		float xcomp;
		int align;

		str_font(COMPOSERFONT);
		bskip(cfmt.composerspace);
		if (cfmt.aligncomposer < 0) {
			xcomp = 0;
			align = A_LEFT;
		} else if (cfmt.aligncomposer == 0) {
			xcomp = lwidth * 0.5;
			align = A_CENTER;
		} else {
			xcomp = lwidth;
			align = A_RIGHT;
		}
		down2 = down1;
		if (author) {
			for (;;) {
				bskip(cfmt.font_tb[COMPOSERFONT].size);
				down2 += cfmt.font_tb[COMPOSERFONT].size;
				a2b("0 0 M ");
				put_inf(author);
				if ((author = author->next) == NULL)
					break;
			}
		}
		if (composer || origin) {
			if (cfmt.aligncomposer >= 0
			 && down1 != down2)
				bskip(down1 - down2);
			s = composer;
			for (;;) {
				bskip(cfmt.font_tb[COMPOSERFONT].size);
				a2b("%.1f 0 M ", xcomp);
				put_inf2r(s,
					  (!s || !s->next) ? origin : NULL,
					  align);
				if (!s)
					break;
				if ((s = s->next) == NULL)
					break;
				down1 += cfmt.font_tb[COMPOSERFONT].size;
			}
			if (down2 > down1)
				bskip(down2 - down1);
		}

		rhythm = rhythm ? NULL : info['R' - 'A'];
		if ((rhythm || area) && cfmt.infoline) {

			/* if only one of rhythm or area then do not use ()'s
			 * otherwise output 'rhythm (area)' */
			str_font(INFOFONT);
			bskip(cfmt.font_tb[INFOFONT].size + cfmt.infospace);
			a2b("%.1f 0 M ", lwidth);
			put_inf2r(rhythm, area, A_RIGHT);
			down1 += cfmt.font_tb[INFOFONT].size + cfmt.infospace;
		}
		down2 = 0;
	} else {
		down2 = cfmt.composerspace;
	}

	/* parts */
	if (info['P' - 'A']
	 && (cfmt.fields[0] & (1 << ('P' - 'A')))) {
		down1 = cfmt.partsspace + cfmt.font_tb[PARTSFONT].size - down1;
		if (down1 > 0)
			down2 += down1;
		if (down2 > 0.01)
			bskip(down2);
		str_font(PARTSFONT);
		a2b("0 0 M ");
		put_inf(info['P' - 'A']);
		down2 = 0;
	}
	bskip(down2 + cfmt.musicspace);
}

/* -- memorize a PS / SVG line -- */
/* 'use' may be:
 *	'g': SVG code
 *	'p': PS code for PS output only
 *	's': PS code for SVG output only
 *	'b': PS code for PS or SVG output
 */
void user_ps_add(char *s, char use)
{
	struct u_ps *t, *r;
	int l;

	if (*s == '\0' || *s == '%')
		return;
	l = strlen(s);
	if (use == 'g') {
		t = malloc(sizeof *user_ps - sizeof user_ps->text + l + 6);
		sprintf(t->text, "%%svg %s", s);
	} else {
		t = malloc(sizeof *user_ps - sizeof user_ps->text + l + 2);
		sprintf(t->text, "%c%s", use, s);
	}
	t->next = NULL;
	if ((r = user_ps) == NULL) {
		user_ps = t;
	} else {
		while (r->next)
			r = r->next;
		r->next = t;
	}
}

/* -- output the user defined postscript sequences -- */
void user_ps_write(void)
{
	struct u_ps *t;
	char *p;

	for (t = user_ps; t; t = t->next) {
		p = t->text;
		switch (*p) {
		case '\001': {		/* PS file */
			FILE *f;
			char line[BSIZE];

			if ((f = fopen(p + 1, "r")) == NULL) {
				error(1, NULL, "Cannot open PS file '%s'",
					&t->text[1]);
			} else {
				while (fgets(line, sizeof line, f))	/* copy the file */
					fputs(line, fout);
				fclose(f);
			}
			continue;
		    }
		case '%':		/* "%svg " = SVG code */
//			if (svg || epsf > 1)
//				svg_write(t->text, strlen(t->text));
			fputs(p + 5, fout);
			fputc('\n', fout);
			continue;
		case 'p':		/* PS code for PS output only */
//			if (secure || svg || epsf > 1)
//				continue;
			break;
		case 'b':		/* PS code for both PS and SVG */
			if (svg || epsf > 1) {
				svg_write(p + 1, strlen(p + 1));
				continue;
			}
//			if (secure)
//				continue;
			break;
		case 's':		/* PS code for SVG output only */
//			if (!svg && epsf <= 1)
//				continue;
			svg_write(p + 1, strlen(&t->text[1]));
			continue;
		}
		fputs(p + 1, fout);
		fputc('\n', fout);
	}
}
abcm2ps-8.5.2/svg.c0000644000175000017500000026660612461144222012130 0ustar  jefjef/*
 * SVG definitions.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2015 Jean-François Moine
 *
 * 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.
 */

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

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#include "abc2ps.h"

enum elt_t {			/* element types */
	VAL,
	STR,
	SEQ,			/* {..} */
	BRK,			/* [..] */
};
struct elt_s {
	struct elt_s *next;
	char type;
	union {
		float v;
		char *s;
		struct elt_s *e;
	} u;
};
struct ps_sym_s {
	char *n;		/* name */
	struct elt_s *e;	/* value */
	int exec;		/* current number of execution */
};
/* -- PostScript tiny interpreter -- */
//jfm test
#define NELTS 2048	/* number of elements per block */
//#define NSYMS 128	/* max number of symbols */
#define NSYMS 512	/* max number of symbols */
static struct elt_s *elts;
static struct elt_s *stack, *free_elt;
static struct ps_sym_s ps_sym[NSYMS];
static int n_sym;
static int ps_error;
static int in_cnt;			/* in [..] or {..} */
static float cx, cy;			/* current point */
static char *path;
static char path_buf[256];

static float xoffs, yoffs;

/* graphical context */
#define DLW 0.7			/* default line width */
static struct gc {
	float xscale, yscale;
	char font_n[64];
	float font_s;
	float linewidth, rotate;
	int rgb;
	char dash[64];
} gcur, gold;
static float x_rot, y_rot;	/* save x and y offset when rotate != 0 */
static struct {
	struct gc gc;
	float cx, cy;
	float xoffs, yoffs;
	float x_rot, y_rot;
} gsave[8];
static int nsave;
static int g;			/* current container */
static int boxend;

/* abcm2ps definitions */
static struct {
	char *def;
	char use;
	char defined;
} def_tb[] = {
#define D_brace 0
{	"<path id=\"brace\" fill=\"currentColor\" d=\"m-2.5 101\n"
	"	c-4.5 -4.6 -7.5 -12.2 -4.4 -26.8\n"
	"	3.5 -14.3 3.2 -21.7 -2.1 -24.2\n"
	"	7.4 2.4 7.3 14.2 3.5 29.5\n"
	"	-2.7 9.5 -1.5 16.2 3 21.5\n"
	"	M-2.5 1c-4.5 4.6 -7.5 12.2 -4.4 26.8\n"
	"	c3.5 14.3 3.2 21.7 -2.1 24.2\n"
	"	7.4 -2.4 7.3 -14.2 3.5 -29.5\n"
	"	-2.7 -9.5 -1.5 -16.2 3 -21.5\"/>\n"},
#define D_utclef 1
{	"<path id=\"utclef\" fill=\"currentColor\" d=\"m-50 -90\n"
	"	c-72 -41 -72 -158 52 -188\n"
	"	150 -10 220 188 90 256\n"
	"	-114 52 -275 0 -293 -136\n"
	"	-15 -181 93 -229 220 -334\n"
	"	88 -87 79 -133 62 -210\n"
	"	-51 33 -94 105 -89 186\n"
	"	17 267 36 374 49 574\n"
	"	6 96 -19 134 -77 135\n"
	"	-80 1 -126 -93 -61 -133\n"
	"	85 -41 133 101 31 105\n"
	"	23 17 92 37 90 -92\n"
	"	-10 -223 -39 -342 -50 -617\n"
	"	0 -90 0 -162 96 -232\n"
	"	56 72 63 230 22 289\n"
	"	-74 106 -257 168 -255 316\n"
	"	9 153 148 185 252 133\n"
	"	86 -65 29 -192 -80 -176\n"
	"	-71 12 -105 67 -59 124\"/>\n"},
#define D_tclef 2
{	"<use id=\"tclef\" transform=\"scale(0.045)\"\n"
	"	xlink:href=\"#utclef\"/>\n", D_utclef},
#define D_stclef 3
{	"<use id=\"stclef\" transform=\"translate(0,-0.6) scale(0.037)\"\n"
	"	xlink:href=\"#utclef\"/>\n", D_utclef},
#define D_ubclef 4
{	"<path id=\"ubclef\" fill=\"currentColor\" d=\"m-200 -87\n"
	"	c124 -35 222 -78 254 -236\n"
	"	43 -228 -167 -246 -192 -103\n"
	"	59 -80 157 22 92 78\n"
	"	-62 47 -115 -22 -106 -88\n"
	"	21 -141 270 -136 274 52\n"
	"	-1 175 -106 264 -322 297\n"
	"	m357 -250\n"
	"	c0 -36 51 -34 51 0\n"
	"	0 37 -51 36 -51 0\n"
	"	m-2 -129\n"
	"	c0 -36 51 -34 51 0\n"
	"	0 38 -51 37 -51 0\"/>\n"},
#define D_bclef 5
{	"<use id=\"bclef\" transform=\"scale(0.045)\"\n"
	"	xlink:href=\"#ubclef\"/>\n", D_ubclef},
#define D_sbclef 6
{	"<use id=\"sbclef\" transform=\"translate(0,-3.5) scale(0.037)\"\n"
	"	xlink:href=\"#ubclef\"/>\n", D_ubclef},
#define D_ucclef 7
{	"<path id=\"ucclef\" fill=\"currentColor\" d=\"\n"
	"	m-51 -264\n"
	"	v262\n"
	"	h-13\n"
	"	v-529\n"
	"	h13\n"
	"	v256\n"
	"	c25 -20 41 -36 63 -109\n"
	"	14 31 13 51 56 70\n"
	"	90 34 96 -266 -41 -185\n"
	"	52 19 27 80 -11 77\n"
	"	-90 -38 33 -176 139 -69\n"
	"	72 79 1 241 -134 186\n"
	"	l-16 39 16 38\n"
	"	c135 -55 206 107 134 186\n"
	"	-106 108 -229 -31 -139 -69\n"
	"	38 -3 63 58 11 77\n"
	"	137 81 131 -219 41 -185\n"
	"	-43 19 -45 30 -56 64\n"
	"	-22 -73 -38 -89 -63 -109\n"
	"	m-99 -267\n"
	"	h57\n"
	"	v529\n"
	"	h-57\n"
	"	v-529\"/>\n"},
#define D_cclef 8
{	"<use id=\"cclef\" transform=\"scale(0.045)\"\n"
	"	xlink:href=\"#ucclef\"/>\n", D_ucclef},
#define D_scclef 9
{	"<use id=\"scclef\" transform=\"translate(0,-2) scale(0.037)\"\n"
	"	xlink:href=\"#ucclef\"/>\n", D_ucclef},
#define D_pclef 10
{	"<path id=\"pclef\" d=\"m-2.7 -2h5.4v-20h-5.4v20\" stroke=\"currentColor\" fill=\"none\" stroke-width=\"1.4\"/>\n"},
#define D_hd 11
{	"<ellipse id=\"hd\" rx=\"4.1\" ry=\"2.9\"\n"
	"	transform=\"rotate(-20)\" fill=\"currentColor\"/>\n"},
#define D_Hd 12
{	"<path id=\"Hd\" fill=\"currentColor\" d=\"m3 -1.6\n"
	"	c-1 -1.8 -7 1.4 -6 3.2\n"
	"	1 1.8 7 -1.4 6 -3.2\n"
	"	m0.5 -0.3\n"
	"	c2 3.8 -5 7.6 -7 3.8\n"
	"	-2 -3.8 5 -7.6 7 -3.8\"/>\n"},
#define D_HD 13
{	"<path id=\"HD\" fill=\"currentColor\" d=\"m-2.7 -1.4\n"
	"	c1.5 -2.8 6.9 0 5.3 2.7\n"
	"	-1.5 2.8 -6.9 0 -5.3 -2.7\n"
	"	m8.3 1.4\n"
	"	c0 -1.5 -2.2 -3 -5.6 -3\n"
	"	-3.4 0 -5.6 1.5 -5.6 3\n"
	"	0 1.5 2.2 3 5.6 3\n"
	"	3.4 0 5.6 -1.5 5.6 -3\"/>\n"},
#define D_HDD 14
{	"<g id=\"HDD\">\n"
	"	<use xlink:href=\"#HD\"/>\n"
	"	<path d=\"m-6 -4v8m12 0v-8\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"</g>\n", D_HD},
#define D_breve 15
{	"<g id=\"breve\" stroke=\"currentColor\" fill=\"none\">\n"
	"	<path d=\"m-6 -2.7h12m0 5.4h-12\" stroke-width=\"2.5\"/>\n"
	"	<path d=\"m-6 -5v10m12 0v-10\"/>\n"
	"</g>\n"},
#define D_longa 16
{	"<g id=\"longa\" stroke=\"currentColor\" fill=\"none\">\n"
	"	<path d=\"m-6 2.7h12m0 -5.4h-12\" stroke-width=\"2.5\"/>\n"
	"	<path d=\"m-6 5v-10m12 0v16\"/>\n"
	"</g>\n"},
#define D_ghd 17
{	"<path id=\"ghd\" fill=\"currentColor\" d=\"m1.7 -1\n"
	"	c-1 -1.7 -4.5 0.2 -3.4 2\n"
	"	1 1.7 4.5 -0.2 3.4 -2\"/>\n"},
#define D_r00 18
{	"<rect id=\"r00\" fill=\"currentColor\"\n"
	"	x=\"-1.6\" y=\"-6\" width=\"3\" height=\"12\"/>\n"},
#define D_r0 19
{	"<rect id=\"r0\" fill=\"currentColor\"\n"
	"	x=\"-1.6\" y=\"-6\" width=\"3\" height=\"6\"/>\n"},
#define D_r1 20
{	"<rect id=\"r1\" fill=\"currentColor\"\n"
	"	x=\"-3.5\" y=\"-6\" width=\"7\" height=\"3\"/>\n"},
#define D_r2 21
{	"<rect id=\"r2\" fill=\"currentColor\"\n"
	"	x=\"-3.5\" y=\"-3\" width=\"7\" height=\"3\"/>\n"},
#define D_r4 22
{	"<path id=\"r4\" fill=\"currentColor\" d=\"m-1 -8.5\n"
	"	l3.6 5.1 -2.1 5.2 2.2 4.3\n"
	"	c-2.6 -2.3 -5.1 0 -2.4 2.6\n"
	"	-4.8 -3 -1.5 -6.9 1.4 -4.1\n"
	"	l-3.1 -4.5 1.9 -5.1 -1.5 -3.5\"/>\n"},
#define D_r8e 23
{	"<path id=\"r8e\" fill=\"currentColor\" d=\"m 0 0\n"
	"	c-1.5 1.5 -2.4 2 -3.6 2\n"
	"	2.4 -2.8 -2.8 -4 -2.8 -1.2\n"
	"	c0 2.7 4.3 2.4 5.9 0.6\"/>\n"},
#define D_r8 24
{	"<g id=\"r8\">\n"
	"	<path d=\"m3.3 -4l-3.4 9.6\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"	<use x=\"3.4\" y=\"-4\" xlink:href=\"#r8e\"/>\n"
	"</g>\n", D_r8e},
#define D_r16 25
{	"<g id=\"r16\">\n"
	"	<path d=\"m3.3 -4l-4 15.6\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"	<use x=\"3.4\" y=\"-4\" xlink:href=\"#r8e\"/>\n"
	"	<use x=\"1.9\" y=\"2\" xlink:href=\"#r8e\"/>\n"
	"</g>\n", D_r8e},
#define D_r32 26
{	"<g id=\"r32\">\n"
	"	<path d=\"m4.8 -10l-5.5 21.6\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"	<use x=\"4.9\" y=\"-10\" xlink:href=\"#r8e\"/>\n"
	"	<use x=\"3.4\" y=\"-4\" xlink:href=\"#r8e\"/>\n"
	"	<use x=\"1.9\" y=\"2\" xlink:href=\"#r8e\"/>\n"
	"</g>\n", D_r8e},
#define D_r64 27
{	"<g id=\"r64\">\n"
	"	<path d=\"m4.8 -10 l-7 27.6\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"	<use x=\"4.9\" y=\"-10\" xlink:href=\"#r8e\"/>\n"
	"	<use x=\"3.4\" y=\"-4\" xlink:href=\"#r8e\"/>\n"
	"	<use x=\"1.9\" y=\"2\" xlink:href=\"#r8e\"/>\n"
	"	<use x=\"0.4\" y=\"8\" xlink:href=\"#r8e\"/>\n"
	"</g>\n", D_r8e},
#define D_r128 28
{	"<g id=\"r128\">\n"
	"	<path d=\"m5.8 -16 l-8.5 33.6\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"	<use x=\"5.9\" y=\"-16\" xlink:href=\"#r8e\"/>\n"
	"	<use x=\"4.4\" y=\"-10\" xlink:href=\"#r8e\"/>\n"
	"	<use x=\"2.9\" y=\"-4\" xlink:href=\"#r8e\"/>\n"
	"	<use x=\"1.4\" y=\"2\" xlink:href=\"#r8e\"/>\n"
	"	<use x=\"0.1\" y=\"8\" xlink:href=\"#r8e\"/>\n"
	"</g>\n", D_r8e},
#define D_mrest 29
{	"<g id=\"mrest\">\n"
	"	<path d=\"m-20 -6v-12m40 0v12\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"	<path d=\"m-20 -12h40\" stroke=\"currentColor\" stroke-width=\"5\" fill=\"none\"/>\n"
	"</g>\n"},
#define D_usharp 30
{	"<path id=\"usharp\" fill=\"currentColor\" d=\"\n"
	"	m136 -702\n"
	"	v890\n"
	"	h32\n"
	"	v-890\n"
	"	m128 840\n"
	"	h32\n"
	"	v-888\n"
	"	h-32\n"
	"	m-232 286\n"
	"	v116\n"
	"	l338 -96\n"
	"	v-116\n"
	"	m-338 442\n"
	"	v116\n"
	"	l338 -98\n"
	"	v-114\"/>\n"},
#define D_uflat 31
{	"<path id=\"uflat\" fill=\"currentColor\" d=\"\n"
	"	m100 -746\n"
	"	h32\n"
	"	v734\n"
	"	l-32 4\n"
	"	m32 -332\n"
	"	c46 -72 152 -90 208 -20\n"
	"	100 110 -120 326 -208 348\n"
	"	m0 -28\n"
	"	c54 0 200 -206 130 -290\n"
	"	-50 -60 -130 -4 -130 34\"/>\n"},
#define D_unat 32
{	"<path id=\"unat\" fill=\"currentColor\" d=\"\n"
	"	m96 -750\n"
	"	h-32\n"
	"	v716\n"
	"	l32 -8\n"
	"	182 -54\n"
	"	v282\n"
	"	h32\n"
	"	v-706\n"
	"	l-34 10\n"
	"	-180 50\n"
	"	v-290\n"
	"	m0 592\n"
	"	v-190\n"
	"	l182 -52\n"
	"	v188\"/>\n"},
#define D_udblesharp 33
{	"<path id=\"udblesharp\" fill=\"currentColor\" d=\"\n"
	"	m240 -282\n"
	"	c40 -38 74 -68 158 -68\n"
	"	v-96\n"
	"	h-96\n"
	"	c0 84 -30 118 -68 156\n"
	"	-40 -38 -70 -72 -70 -156\n"
	"	h-96\n"
	"	v96\n"
	"	c86 0 120 30 158 68\n"
	"	-38 38 -72 68 -158 68\n"
	"	v96\n"
	"	h96\n"
	"	c0 -84 30 -118 70 -156\n"
	"	38 38 68 72 68 156\n"
	"	h96\n"
	"	v-96\n"
	"	c-84 0 -118 -30 -158 -68\"/>\n"},
#define D_udbleflat 34
{	"<path id=\"udbleflat\" fill=\"currentColor\" d=\"\n"
	"	m20 -746\n"
	"	h24\n"
	"	v734\n"
	"	l-24 4\n"
	"	m24 -332\n"
	"	c34 -72 114 -90 156 -20\n"
	"	75 110 -98 326 -156 348\n"
	"	m0 -28\n"
	"	c40 0 150 -206 97 -290\n"
	"	-37 -60 -97 -4 -97 34\n"
	"	m226 -450\n"
	"	h24\n"
	"	v734\n"
	"	l-24 4\n"
	"	m24 -332\n"
	"	c34 -72 114 -90 156 -20\n"
	"	75 110 -98 326 -156 348\n"
	"	m0 -28\n"
	"	c40 0 150 -206 97 -290\n"
	"	-37 -60 -97 -4 -97 34\"/>\n"},
#define D_sh0 35
{	"<use id=\"sh0\" transform=\"translate(-4,5) scale(0.018)\"\n"
	"	xlink:href=\"#usharp\"/>\n", D_usharp},
#define D_ft0 36
{	"<use id=\"ft0\" transform=\"translate(-3.5,3.5) scale(0.018)\"\n"
	"	xlink:href=\"#uflat\"/>\n", D_uflat},
#define D_nt0 37
{	"<use id=\"nt0\" transform=\"translate(-3,5) scale(0.018)\"\n"
	"	xlink:href=\"#unat\"/>\n", D_unat},
#define D_dsh0 38
{	"<use id=\"dsh0\" transform=\"translate(-4,5) scale(0.018)\"\n"
	"	xlink:href=\"#udblesharp\"/>\n", D_udblesharp},
#define D_dft0 39
{	"<use id=\"dft0\" transform=\"translate(-4,3.5) scale(0.018)\"\n"
	"	xlink:href=\"#udbleflat\"/>\n", D_udbleflat},
#define D_sh1 40
{	"<g id=\"sh1\">\n"
	"	<path d=\"M0 7.8v-15.4\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"	<path fill=\"currentColor\" d=\"M-1.8 2.7l3.6 -1.1v2.2l-3.6 1.1v-2.2z\n"
	"		M-1.8 -3.7l3.6 -1.1v2.2l-3.6 1.1v-2.2\"/>\n"
	"</g>\n"},
#define D_sh513 41
{	"<g id=\"sh513\">\n"
	"	<path d=\"M-2.5 8.7v-15.4M0 7.8v-15.4M2.5 6.9v-15.4\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"	<path fill=\"currentColor\" d=\"M-3.7 3.1l7.4 -2.2v2.2l-7.4 2.2v-2.2z\n"
	"		M-3.7 -3.2l7.4 -2.2v2.2l-7.4 2.2v-2.2\"/>\n"
	"</g>\n"},
#define D_ft1 42
{	"<g id=\"ft1\" transform=\"scale(-1,1)\">\n"
	"	<use xlink:href=\"#ft0\"/>\n"
	"</g>\n", D_ft0},
#define D_ft513 43
{	"<g id=\"ft513\">\n"
	"	<path fill=\"currentColor\" d=\"M0.6 -2.7\n"
	"		c-5.7 -3.1 -5.7 3.6 0 6.7c-3.9 -4 -4 -7.6 0 -5.8\n"
	"		M1 -2.7c5.7 -3.1 5.7 3.6 0 6.7c3.9 -4 4 -7.6 0 -5.8\"/>\n"
	"	<path d=\"M1.6 3.5v-13M0 3.5v-13\" stroke=\"currentColor\" fill=\"none\" stroke-width=\".6\"/>\n"
	"</g>\n"},
#define D_pshhd 44
{	"<g id=\"pshhd\">\n"
	"	<use xlink:href=\"#dsh0\"/>\n"
	"</g>\n", D_dsh0},
#define D_pfthd 45
{	"<g id=\"pfthd\">\n"
	"	<use xlink:href=\"#dsh0\"/>\n"
	"	<circle r=\"4\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"</g>\n", D_dsh0},
#define D_csig 46
{	"<path id=\"csig\" fill=\"currentColor\" d=\"\n"
	"	m1 -17.3\n"
	"	c0.9 0 2.3 0.7 2.4 2.2\n"
	"	-1.2 -2 -3.6 0.1 -1.6 1.7\n"
	"	2 1 3.8 -3.5 -0.8 -4.7\n"
	"	-2 -0.4 -6.4 1.3 -5.8 7\n"
	"	0.4 6.4 7.9 6.8 9.1 0.7\n"
	"	-2.3 5.6 -6.7 5.1 -6.8 0\n"
	"	-0.5 -4.4 0.7 -7.5 3.5 -6.9\"/>\n"},
#define D_ctsig 47
{	"<g id=\"ctsig\">\n"
	"	<use xlink:href=\"#csig\"/>\n"
	"	<path d=\"m0 -4v-16\" stroke=\"currentColor\"/>\n"
	"</g>\n", D_csig},
#define D_pmsig 48
{	"<path id=\"pmsig\" stroke=\"currentColor\" stroke-width=\"0.8\" fill=\"none\"\n"
	"	d=\"M0 -7a5 5 0 0 1 0 -10a5 5 0 0 1 0 10\"/>\n"},
#define D_pMsig 49
{	"<g id=\"pMsig\">\n"
	"	<use xlink:href=\"#pmsig\"/>\n"
	"	<path fill=\"currentColor\" d=\"M0 -10a2 2 0 0 1 0 -4a2 2 0 0 1 0 4\"/>\n"
	"</g>\n", D_pmsig},
#define D_imsig 50
{	"<path id=\"imsig\" stroke=\"currentColor\" stroke-width=\"0.8\" fill=\"none\"\n"
	"	d=\"M0 -7a5 5 0 1 1 0 -10\"/>\n"},
#define D_iMsig 51
{	"<g id=\"iMsig\">\n"
	"	<use xlink:href=\"#imsig\"/>\n"
	"	<path fill=\"currentColor\" d=\"M0 -10a2 2 0 0 1 0 -4a2 2 0 0 1 0 4\"/>\n"
	"</g>\n", D_imsig},
#define D_hl 52
{	"<path id=\"hl\" stroke=\"currentColor\" d=\"m-6 0h12\"/>\n"},
#define D_hl1 53
{	"<path id=\"hl1\" stroke=\"currentColor\" d=\"m-7 0h14\"/>\n"},
#define D_hl2 54
{	"<path id=\"hl2\" stroke=\"currentColor\" d=\"m-9 0h18\"/>\n"},
#define D_ghl 55
{	"<path id=\"ghl\" stroke=\"currentColor\" fill=\"none\" d=\"m-3 0h6\"/>\n"},
#define D_rdots 56
{	"<g id=\"rdots\" fill=\"currentColor\">\n"
	"	<circle cx=\"0\" cy=\"-9\" r=\"1.2\"/>\n"
	"	<circle cx=\"0\" cy=\"-15\" r=\"1.2\"/>\n"
	"</g>\n"},
#define D_srep 57
{	"<path id=\"srep\" fill=\"currentColor\" d=\"M-1 -6l11 -12h3l-11 12h-3\"/>\n"},
#define D_mrep 58
{	"<path id=\"mrep\" fill=\"currentColor\"\n"
	"    d=\"M-5 -16.5a1.5 1.5 0 0 1 0 3a1.5 1.5 0 0 1 0 -3\n"
	"	M4.5 -10a1.5 1.5 0 0 1 0 3a1.5 1.5 0 0 1 0 -3\n"
	"	M-7 -6l11 -12h3l-11 12h-3\"/>\n"},
#define D_mrep2 59
{	"<g id=\"mrep2\" fill=\"currentColor\">\n"
	"	<path d=\"M-5.5 -19.5a1.5 1.5 0 0 1 0 3a1.5 1.5 0 0 1 0 -3\n"
	"		M5 -7.5a1.5 1.5 0 0 1 0 3a1.5 1.5 0 0 1 0 -3\"/>\n"
	"	<path d=\"M-7 -4l14 -10m-14 4l14 -10\" stroke=\"currentColor\" stroke-width=\"1.8\" fill=\"none\"/>\n"
	"</g>\n"},
#define D_accent 60
{	"<g id=\"accent\" stroke=\"currentColor\" fill=\"none\">\n"
	"	<path d=\"m-4 0l8 -2l-8 -2\" stroke-width=\"1.2\"/>\n"
	"</g>\n"},
#define D_umrd 61
{	"<path id=\"umrd\" fill=\"currentColor\" d=\"m0 -4\n"
	"	l2.2 -2.2 2.1 2.9 0.7 -0.7 0.2 0.2\n"
	"	-2.2 2.2 -2.1 -2.9 -0.7 0.7\n"
	"	-2.2 2.2 -2.1 -2.9 -0.7 0.7 -0.2 -0.2\n"
	"	2.2 -2.2 2.1 2.9 0.7 -0.7\"/>\n"},
#define D_lmrd 62
{	"<g id=\"lmrd\">\n"
	"	<use xlink:href=\"#umrd\"/>\n"
	"	<line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"-8\" stroke=\"currentColor\" stroke-width=\".6\"/>\n"
	"</g>\n", D_umrd},
#define D_grm 63
{	"<path id=\"grm\" fill=\"currentColor\" d=\"\n"
	"	m-5 -2.5\n"
	"	c5 -8.5 5.5 4.5 10 -2\n"
	"	-5 8.5 -5.5 -4.5 -10 2\"/>\n"},
#define D_stc 64
{	"<circle id=\"stc\" fill=\"currentColor\" cx=\"0\" cy=\"-3\" r=\"1.2\"/>\n"},
#define D_sld 65
{	"<path id=\"sld\" fill=\"currentColor\" d=\"\n"
	"	m-7.2 4.8\n"
	"	c1.8 0.7 4.5 -0.2 7.2 -4.8\n"
	"	-2.1 5 -5.4 6.8 -7.6 6\"/>\n"},
#define D_emb 66
{	"<path id=\"emb\" d=\"m-2.5 -3h5\" stroke-width=\"1.2\" stroke-linecap=\"round\" stroke=\"currentColor\"/>\n"},
#define D_hld 67
{	"<g id=\"hld\" fill=\"currentColor\">\n"
	"	<circle cx=\"0\" cy=\"-3\" r=\"1.3\"/>\n"
	"	<path d=\"m-7.5 -1.5\n"
	"		c0 -11.5 15 -11.5 15 0\n"
	"		h-0.25\n"
	"		c-1.25 -9 -13.25 -9 -14.5 0\"/>\n"
	"</g>\n"},
#define D_cpu 68
{	"<path id=\"cpu\" fill=\"currentColor\" d=\"\n"
	"	m-6 0\n"
	"	c0.4 -7.3 11.3 -7.3 11.7 0\n"
	"	c-1.3 -6 -10.4 -6 -11.7 0\"/>\n"},
#define D_upb 69
{	"<path id=\"upb\" stroke=\"currentColor\" fill=\"none\" d=\"\n"
	"	m-2.6 -9.4\n"
	"	l2.6 8.8\n"
	"	2.6 -8.8\"/>\n"},
#define D_dnb 70
{	"<g id=\"dnb\">\n"
	"	<path d=\"M-3.2 -2v-7.2m6.4 0v7.2\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"	<path d=\"M-3.2 -6.8v-2.4l6.4 0v2.4\" fill=\"currentColor\"/>\n"
	"</g>\n"},
#define D_sgno 71
{	"<g id=\"sgno\">\n"
	"    <path fill=\"currentColor\" d=\"m0 -3\n"
	"	c1.5 1.7 6.4 -0.3 3 -3.7\n"
	"	-10.4 -7.8 -8 -10.6 -6.5 -11.9\n"
	"	4 -1.9 5.9 1.7 4.2 2.6\n"
	"	-1.3 0.7 -2.9 -1.3 -0.7 -2\n"
	"	-1.5 -1.7 -6.4 0.3 -3 3.7\n"
	"	10.4 7.8 8 10.6 6.5 11.9\n"
	"	-4 1.9 -5.9 -1.7 -4.2 -2.6\n"
	"	1.3 -0.7 2.9 1.3 0.7 2\"/>\n"
	"    <line x1=\"-6\" y1=\"-4.2\" x2=\"6.6\" y2=\"-16.8\" stroke=\"currentColor\"/>\n"
	"    <circle cx=\"-6\" cy=\"-10\" r=\"1.2\"/>\n"
	"    <circle cx=\"6\" cy=\"-11\" r=\"1.2\"/>\n"
	"</g>\n"},
#define D_coda 72
{	"<g id=\"coda\" stroke=\"currentColor\" fill=\"none\">\n"
	"	<path d=\"m0 -2v-20m-10 10h20\"/>\n"
	"	<circle cx=\"0\" cy=\"-12\" r=\"6\" stroke-width=\"1.7\"/>\n"
	"</g>\n"},
#define D_dplus 73
{	"<path id=\"dplus\" stroke=\"currentColor\" stroke-width=\"1.7\"\n"
	"	d=\"m0 -0.5v-6m-3 3h6\"/>\n"},
#define D_lphr 74
{	"<path id=\"lphr\" stroke=\"currentColor\" stroke-width=\"1.2\"\n"
	"	d=\"m0 0v18\"/>\n"},
#define D_mphr 75
{	"<path id=\"mphr\" stroke=\"currentColor\" stroke-width=\"1.2\"\n"
	"	d=\"m0 0v12\"/>\n"},
#define D_sphr 76
{	"<path id=\"sphr\" stroke=\"currentColor\" stroke-width=\"1.2\"\n"
	"	d=\"m0 0v6\"/>\n"},
#define D_opend 77
{	"<circle id=\"opend\" stroke=\"currentColor\" fill=\"none\"\n"
	"	cx=\"0\" cy=\"-3\" r=\"2.5\"/>\n"},
#define D_snap 78
{	"<path id=\"snap\" stroke=\"currentColor\" fill=\"none\"\n"
	"	d=\"M-3 -6\n"
	"		c0 -5 6 -5 6 0\n"
	"		c0 5 -6 5 -6 0\n"
	"		M0 -5v6\"/>\n"},
#define D_thumb 79
{	"<path id=\"thumb\" stroke=\"currentColor\" fill=\"none\"\n"
	"	d=\"M-2.5 -7\n"
	"		c0 -6 5 -6 5 0\n"
	"		c0 6 -5 6 -5 0\n"
	"		M-2.5 -9v4\"/>\n"},
#define D_turn 80
{	"<path id=\"turn\" fill=\"currentColor\" d=\"\n"
	"	m5.2 -8\n"
	"	c1.4 0.5 0.9 4.8 -2.2 2.8\n"
	"	l-4.8 -3.5\n"
	"	c-3 -2 -5.8 1.8 -3.6 4.4\n"
	"	1 1.1 2 0.8 2.1 -0.1\n"
	"	0.1 -0.9 -0.7 -1.2 -1.9 -0.6\n"
	"	-1.4 -0.5 -0.9 -4.8 2.2 -2.8\n"
	"	l4.8 3.5\n"
	"	c3 2 5.8 -1.8 3.6 -4.4\n"
	"	-1 -1.1 -2 -0.8 -2.1 0.1\n"
	"	-0.1 0.9 0.7 1.2 1.9 0.6\"/>\n"},
#define D_turnx 81
{	"<g id=\"turnx\">\n"
	"	<use xlink:href=\"#turn\"/>\n"
	"	<path d=\"M0 -1.5v-9\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"</g>\n", D_turn},
#define D_wedge 82
{	"<path id=\"wedge\" fill=\"currentColor\" d=\"M0 -1l-1.5 -5h3l-1.5 5\"/>\n"},
#define D_ltr 83
{	"<path id=\"ltr\" fill=\"currentColor\"\n"
	"    d=\"m0 -0.4c2 -1.5 3.4 -1.9 3.9 0.4\n"
	"	c0.2 0.8 0.7 0.7 2.1 -0.4\n"
	"	v0.8c-2 1.5 -3.4 1.9 -3.9 -0.4\n"
	"	c-0.2 -0.8 -0.7 -0.7 -2.1 0.4z\"/>\n"},
#define D_custos 84
{	"<g id=\"custos\">\n"
	"	<path d=\"M-4 0l2 2.5l2 -2.5l2 2.5l2 -2.5\n"
	"		l-2 -2.5l-2 2.5l-2 -2.5l-2 2.5\" fill=\"currentColor\"/>\n"
	"	<path d=\"M3.5 0l5 -7\" stroke=\"currentColor\" fill=\"none\"/>\n"
	"</g>\n"},
#define D_showerror 85
{	"<circle id=\"showerror\" r=\"30\" stroke=\"#ffc0c0\" stroke-width=\"2.5\" fill=\"none\"/>\n"},
#define D_sfz 86
{	"<text id=\"sfz\" font-family=\"serif\" font-size=\"14\" font-style=\"italic\" font-weight=\"normal\"\n"
	"	x=\"-5\" y=\"-7\">s<tspan\n"
	"	font-size=\"16\" font-weight=\"bold\">f</tspan>z</text>\n"},
#define D_trl 87
{	"<text id=\"trl\" font-family=\"serif\" font-size=\"16\" font-style=\"italic\"\n"
	"	x=\"-2\" y=\"-4\">tr</text>\n"},
#define D_marcato 88
{	"<path id=\"marcato\" d=\"m-3 0l3 -7l3 7l-1.5 0l-1.8 -4.2l-1.7 4.2\"/>\n"},
};

/* PS functions */
static void elts_link(struct elt_s *e)
{
	int i;

	/* set the linkages - the first element is the link to the next block */
	for (i = 1; i < NELTS - 1; i++) {
		e[i].next = &e[i + 1];
		if (e[i].type == STR)
			free(e[i].u.s);
		e[i].type = VAL;
	}
	e[NELTS - 1].next = NULL;
}

/* (re)initialize all PS elements */
static void elts_reset(void)
{
	struct elt_s *e;

	if (!elts)
		elts = calloc(sizeof *elts, NELTS);
	elts_link(elts);
	free_elt = elts + 1;

	/* link all blocks */
	for (e = elts; e->u.e; e = e->u.e) {
		elts_link(e->u.e);
		e[NELTS - 1].next = e->u.e;
	}
}

static struct elt_s *elt_new(void)
{
	struct elt_s *e;

	e = free_elt;
	if (!e) {
		e = calloc(sizeof *e, NELTS);
		if (!e) {
			fprintf(stderr, "svg: elt_new out of memory\n");
			ps_error = 1;
			return e;
		}
		elts_link(e);
		e->u.e = elts;
		elts = e;
		e++;
	}
	free_elt = e->next;
	e->next = NULL;
	e->type = VAL;
	return e;
}

static void elt_free(struct elt_s *e)
{
	struct elt_s *e2;

	e->next = free_elt;
	free_elt = e;
	switch (e->type) {
	case STR:
		free(e->u.s);
		e->type = VAL;
		e->u.v = 0;
		break;
	case SEQ:
	case BRK:
		e2 = e->u.e;
		e->type = VAL;
		e->u.v = 0;
		while (e2) {
			e = e2->next;
			elt_free(e2);
			e2 = e;
		}
		break;
	}
}

static struct elt_s *elt_dup(struct elt_s *e)
{
	struct elt_s *e2, *e3, *e4;

	e2 = elt_new();
	if (!e2)
		return e2;
	e2->type = e->type;
	switch (e->type) {
	case VAL:
		e2->u.v = e->u.v;
		break;
	case STR:
		e2->u.s = strdup(e->u.s);
		break;
	case SEQ:
	case BRK:
		e = e->u.e;
		if (!e) {
			e2->u.e = NULL;
			break;
		}
		e3 = e2->u.e = elt_dup(e);
		if (!e3)
			break;
		for (;;) {
			e = e->next;
			if (!e)
				break;
			e4 = elt_dup(e);
			if (!e4)
				break;
			e3->next = e4;
			e3 = e4;
		}
		e3->next = NULL;
		break;
	}
	return e2;
}

static void elt_dump(struct elt_s *e)
{
	int type;

	type = e->type;
	switch (type) {
	case VAL:
		fprintf(stderr, " %.2f", e->u.v);
		break;
	case STR:
		fprintf(stderr, " %s", e->u.s);
		if (e->u.s[0] == '(')
			fprintf(stderr, ")");
		break;
	case SEQ:
	case BRK:
		fprintf(stderr, type == SEQ ? " {" : " [");
		e = e->u.e;
		while (e) {
			elt_dump(e);
			e = e->next;
		}
		fprintf(stderr, type == SEQ ? " }" : " ]");
	}
}

static void elt_lst_dump(struct elt_s *e)
{
	do {
		elt_dump(e);
		e = e->next;
	} while (e);
}

static struct ps_sym_s *ps_sym_lookup(char *name)
{
	struct ps_sym_s *ps;

	if (n_sym == 0)
		return NULL;
	ps = &ps_sym[n_sym];
	for (;;) {
		ps--;
		if (strcmp(ps->n, name) == 0)
			break;
		if (ps == ps_sym)
			return NULL;
	}
	return ps;
}

static struct ps_sym_s *ps_sym_def(char *name, struct elt_s *e)
{
	struct ps_sym_s *ps;

	ps = ps_sym_lookup(name);
	if (ps) {
		elt_free(ps->e);
	} else {
		if (n_sym >= NSYMS) {
			fprintf(stderr, "svg: Too many PS symbols\n");
			ps_error = 1;
			return NULL;
		}
		ps = &ps_sym[n_sym++];
		ps->n = strdup(name);
	}
	ps->e = e;
	ps->exec = 0;
	return ps;
}

static void push(struct elt_s *e)
{
	e->next = stack;
	stack = e;
}

static void stack_dump(void)
{
	fprintf(stderr, "stack:");
	if (stack)
		elt_lst_dump(stack);
	else
		fprintf(stderr, "(empty)");
	fprintf(stderr, "\n");
}

static struct elt_s *pop(int type)
{
	struct elt_s *e;

	e = stack;
	if (!e) {
		fprintf(stderr, "svg pop: Stack empty\n");
		ps_error = 1;
		return NULL;
	}
	if (e->type != type) {
		fprintf(stderr, "svg pop: Bad element type %d != %d\n",
			e->type, type);
		stack_dump();
		ps_error = 1;
		return NULL;
	}
	stack = e->next;
	return e;
}

static float pop_free_val(void)
{
	struct elt_s *e;

	e = pop(VAL);
	if (!e)
		return 0;
	e->next = free_elt;
	free_elt = e;
	return e->u.v;
}

static char *pop_free_str(void)
{
	struct elt_s *e;
	char *s;

	e = pop(STR);
	if (!e)
		return NULL;
	s = e->u.s;
	e->type = VAL;
	e->next = free_elt;
	free_elt = e;
	return s;
}

/* PS condition code */
#define C_EQ 0
#define C_NE 1
#define C_GT 2
#define C_GE 3
#define C_LT 4
#define C_LE 5
static void cond(int type)
{
	float v;
	char *s, *s2;

	if (!stack || !stack->next) {
		fprintf(stderr, "svg: Stack underflow in condition\n");
		ps_error = 1;
		return;
	}

	/* string compare */
	if (stack->type == STR && stack->next->type == STR) {
		s = pop_free_str();
		s2 = stack->u.s;
		switch (type) {
		case C_EQ:
			stack->u.v = strcmp(s2, s) == 0;
			break;
		case C_NE:
			stack->u.v = strcmp(s2, s) != 0;
			break;
		default:
			fprintf(stderr, "svg: String condition not treated\n");
			break;
		}
		free(s);
		free(s2);
		stack->type = VAL;
		return;
	}

	/* special case when 1 character strings */
	if (stack->type == STR) {
		s = stack->u.s;
		stack->u.v = s[1];
		free(s);
		stack->type = VAL;
	}
	if (stack->next->type == STR) {
		s = stack->next->u.s;
		stack->next->u.v = s[1];
		free(s);
		stack->next->type = VAL;
	}
	v = pop_free_val();
	if (stack->type != VAL) {
		fprintf(stderr, "svg: Bad type for condition\n");
		ps_error = 1;
		return;
	}
	switch (type) {
	case C_EQ:
		stack->u.v = stack->u.v == v;
		break;
	case C_NE:
		stack->u.v = stack->u.v != v;
		break;
	case C_GT:
		stack->u.v = stack->u.v > v;
		break;
	case C_GE:
		stack->u.v = stack->u.v >= v;
		break;
	case C_LT:
		stack->u.v = stack->u.v < v;
		break;
	case C_LE:
		stack->u.v = stack->u.v <= v;
		break;
	}
}

/* -- output information about the generation in the XHTML/SVG headers -- */
static void gen_info(void)
{
	unsigned i;
	time_t ltime;

	time(&ltime);
#ifndef WIN32
	strftime(tex_buf, TEX_BUF_SZ, "%b %e, %Y %H:%M", localtime(&ltime));
#else
	strftime(tex_buf, TEX_BUF_SZ, "%b %#d, %Y %H:%M", localtime(&ltime));
#endif
	fprintf(fout, "<!-- CreationDate: %s -->\n"
			"<!-- CommandLine:",
			tex_buf);

	for (i = 1; i < (unsigned) s_argc; i++) {
		char *p;
		int space;

		p = s_argv[i];
		space = strchr(p, ' ') != NULL || strchr(p, '\n') != NULL;
		fputc(' ', fout);
		if (space)
			fputc('\'', fout);

		/* cannot have '--' inside comment ! */
		if (*p == '-' && p[1] == '-') {
			fputs("-\\", fout);
			p++;
		}
		fputs(p, fout);
		if (space)
			fputc('\'', fout);
	}
	fputs(" -->\n", fout);
}

/* -- output the symbol definitions -- */
void define_svg_symbols(char *title, int num, float w, float h)
{
	char *s;
	unsigned i;
	static const char *svg_head =
		"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n"
		"\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
		"\txml:space='preserve' color=\"black\"\n"
		"\twidth=\"%.2fin\" height=\"%.2fin\" viewBox=\"0 0 %.0f %.0f\">\n"
		"<title>%s %s %d</title>\n";

	xoffs = yoffs = x_rot = y_rot = 0;
	memset(&gcur, 0, sizeof gcur);
	gcur.xscale = gcur.yscale = 1;
	gcur.linewidth = DLW;
	memcpy(&gold, &gcur, sizeof gold);
	nsave = 0;
	for (i = 0; i < sizeof def_tb / sizeof def_tb[0]; i++)
		def_tb[i].defined = 0;

	if (svg == 2) {			/* if XHTML */
		if (file_initialized <= 0) {
			if ((s = strrchr(in_fname, DIRSEP)) == NULL)
				s = in_fname;
			else
				s++;
			fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
				"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1.dtd\">\n"
				"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
				"<head>\n"
				"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n"
				"<meta name=\"generator\" content=\"abcm2ps-" VERSION "\"/>\n",
				fout);
			gen_info();
			fprintf(fout,
				"<style type=\"text/css\">\n"
				"\tbody {margin:0; padding:0; border:0;");
			if (cfmt.bgcolor && cfmt.bgcolor[0] != '\0')
				fprintf(fout, " background-color:%s",
						cfmt.bgcolor);
			fprintf(fout,
				"}\n"
				"\t@page {margin:0;}\n"
//				"\tdiv.nobrk {page-break-inside: avoid}\n"
				"\ttext {white-space:pre; fill:currentColor}\n"
				"\tsvg {display:block}\n"
//				"\tmedia print {div.newpage {page-break-before: always}}\n"
				"</style>\n"
				"<title>%s</title>\n"
				"</head>\n"
				"<body>\n",
				s);
		} else {
			fputs("<br/>\n", fout);
		}
//		fputs("<p>\n", fout);
		fprintf(fout, svg_head, w / 72, h / 72, w, h, title, "page", num);
//		if (cfmt.bgcolor && cfmt.bgcolor[0] != '\0')
//			fprintf(fout,
//				"<rect width=\"100%%\" height=\"100%%\" fill=\"%s\"/>\n",
//				cfmt.bgcolor);
	} else {				/* -g, -v or -z */
		if (epsf != 3) {
			if (fout != stdout)
				fputs("<?xml version=\"1.0\" standalone=\"no\"?>\n"
					"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n"
					"\t\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
					fout);
//			else if (svg)
//				fputs("<p>\n", fout);
		}
		fprintf(fout, svg_head, w / 72, h / 72, w, h, title,
			epsf ? "tune" : "page", num);
		fputs("<!-- Creator: abcm2ps-" VERSION " -->\n", fout);
		gen_info();
		if (cfmt.bgcolor && cfmt.bgcolor[0] != '\0')
			fprintf(fout,
				"<rect width=\"100%%\" height=\"100%%\" fill=\"%s\"/>\n",
				cfmt.bgcolor);
	}

	/* if new page, done */
	if (file_initialized > 0)
		return;

	elts_reset();
	n_sym = 0;

	in_cnt = 0;
	path = NULL;
	ps_error = 0;

	s = strdup("/defl 0 def\n"
		   "/svg 1 def\n"
		   "/gsc{gsave y T .7 dup scale 0 0}def\n");
	svg_write(s, strlen(s));
	free(s);
}

static void selfont(int back)
{
	char *p;
	int i, imin, flags;

	if (gcur.font_n[0] == '\0')
		return;
	flags = 0;
	imin = 255;
	p = strchr(gcur.font_n, '-');
	if (p) {
		imin = p - gcur.font_n;
		flags = 1;
	}
	p = strstr(gcur.font_n, "Bold");
	if (p) {
		i = p - gcur.font_n;
		if (imin > i)
			imin = i;
		flags |= 2;
	}
	p = strstr(gcur.font_n, "Italic");
	if (p) {
		i = p - gcur.font_n;
		if (imin > i)
			imin = i;
		flags |= 4;
	}
	p = strstr(gcur.font_n, "Oblique");
	if (p) {
		i = p - gcur.font_n;
		if (imin > i)
			imin = i;
		flags |= 8;
	}
	if (flags == 0) {
		fprintf(fout, " font-family=\"%s\" font-size=\"%.2f\"",
			gcur.font_n, gcur.font_s);
	} else {
		fprintf(fout, " font-family=\"%.*s\" font-size=\"%.2f\"",
			imin, gcur.font_n, gcur.font_s);
		if (flags & 2)
			fprintf(fout, " font-weight=\"bold\"");
		if (flags & 4)
			fprintf(fout, " font-style=\"italic\"");
		if (flags & 8)
			fprintf(fout, " font-style=\"oblique\"");
	}

	if (!back)
		return;
	if (!(flags & 1)
	 && strstr(gold.font_n, "Bold") != NULL)
		fprintf(fout, " font-weight=\"normal\"");
	if (!(flags & 6)
	 && (strstr(gold.font_n, "Italic") != NULL
	  || strstr(gold.font_n, "Oblique") != NULL))
		fprintf(fout, " font-style=\"normal\"");
}

static float strw(char *s)
{
	unsigned char c;
	float w;

	w = 0;
	for (;;) {
		c = (unsigned char) *s++;
		if (c == '\0')
			break;
		w += cwid(c) * 1.1;
	}
	return w * gcur.font_s;
}

/* define the global container */
static void setg(int newg);
static void defg1(void)
{
	setg(0);
	fputs("<g", fout);
	if (gcur.xscale != 1 || gcur.yscale != 1 || gcur.rotate != 0) {
		fprintf(fout, " transform=\"");
		if (gcur.xscale != 1 || gcur.yscale != 1) {
			if (gcur.xscale == gcur.yscale)
				fprintf(fout, "scale(%.3f)", gcur.xscale);
			else
				fprintf(fout, "scale(%.3f,%.3f)",
						gcur.xscale, gcur.yscale);
		}
		if (gcur.rotate != 0) {
			if (xoffs != 0 || yoffs != 0) {
				fprintf(fout, " translate(%.2f, %.2f)",
						xoffs, yoffs);
				x_rot = xoffs;
				y_rot = yoffs;
				xoffs = 0;
				yoffs = 0;
			}
			fprintf(fout, " rotate(%.2f)",
					gcur.rotate);
		}
		fputs("\"", fout);
	}
	if (gcur.linewidth != 1)
		fprintf(fout, " stroke-width=\"%.2f\"", gcur.linewidth);
	selfont(0);
	if (gcur.rgb != 0)
		fprintf(fout, " style=\"color:#%06x\"", gcur.rgb);
//jfm test
//	fprintf(fout, "%s>\n", gcur.dash);
	fprintf(fout, ">\n");
	g = 1;
	memcpy(&gold, &gcur, sizeof gold);
}

/*
 * set the state of the containers
 * state:
 *	0: no container
 *	1: graphical container
 *	2: graphical container and text
 * newg:
 *	0: close both the text and the graphical container
 *	1: close only the text and reset the graphical container
 */
static void setg(int newg)
{
#if 0 //path change
	if (in_path) {
		fputs("\"/>\n", fout);
		fprintf(stderr, "svg setg: No stroke nor fill\n");
//		ps_error = 1;
		in_path = 0;
	}
#endif
	if (g == 2) {
		fputs("</text>\n", fout);
		g = 1;
	}
	if (newg == 0) {
		if (g != 0) {
			fputs("</g>\n", fout);
			if (gcur.rotate != 0) {
				xoffs = x_rot;
				yoffs = y_rot;
				x_rot = 0;
				y_rot = 0;
			}
			g = 0;
		}
	} else if (memcmp(&gcur, &gold, sizeof gcur) != 0) {
		defg1();
	}
}

/* graphic path */
static void path_print(char *fmt, ...)
{
	va_list args;
	char *p;

	va_start(args, fmt);
	vsnprintf(path_buf, sizeof path_buf, fmt, args);
	va_end(args);
	if (!path) {
		path = malloc(strlen(path_buf) + 1);
		p = path;
	} else {
		path = realloc(path, strlen(path) + strlen(path_buf) + 1);
		p = path + strlen(path);
	}
	if (!path) {
		fprintf(stderr, "Out of memory.\n");
		exit(EXIT_FAILURE);
	}
	strcpy(p, path_buf);
}

static void path_def(void)
{
	if (path)
		return;
	setg(1);
	path_print("<path d=\"m%.2f %.2f\n", xoffs + cx, yoffs - cy);
}

static void path_end(void)
{
	setg(1);
	fputs(path, fout);
	free(path);
	path = NULL;
}

static void def_use(int def)
{
	int i;

	gcur.linewidth = DLW;
	setg(1);
	if (def_tb[def].defined)
		return;
	def_tb[def].defined = 1;
	fputs("<defs>\n", fout);
	i = def_tb[def].use;
	while (i != 0 && !def_tb[i].defined) {
		def_tb[i].defined = 1;
		fputs(def_tb[i].def, fout);
		i = def_tb[i].use;
	}
	fputs(def_tb[def].def, fout);
	fputs("</defs>\n", fout);
}

static void xysym(char *op, int use)
{
	float x, y;

	def_use(use);
	y = yoffs - pop_free_val();
	x = xoffs + pop_free_val();
	fprintf(fout, "<use x=\"%.2f\" y=\"%.2f\" xlink:href=\"#%s\"/>\n",
		x, y, op);
}

static void setxory(char *s, float v)
{
	struct elt_s *e;
	struct ps_sym_s *sym;

	sym = ps_sym_lookup(s);
	if (!sym || sym->e->type != VAL) {
		e = elt_new();
		if (!e)
			return;
		e->type = VAL;
		sym = ps_sym_def(s, e);
		if (!sym)
			return;
	}
	sym->e->u.v = v;
}

static void setxysym(char *op, int use)
{
	float x, y;

	y = pop_free_val();
	x = pop_free_val();
	setxory("x", x);
	setxory("y", y);
	def_use(use);
	fprintf(fout, "<use x=\"%.2f\" y=\"%.2f\" xlink:href=\"#%s\"/>\n",
		xoffs + x, yoffs - y, op);
}

/*  gua gda (acciaccatura) */
static void acciac(char *op)
{
	struct ps_sym_s *sym;
	float x, y, dx, dy;

	setg(1);
	dy = pop_free_val();
	dx = pop_free_val();
	sym = ps_sym_lookup("x");
	x = xoffs + sym->e->u.v;
	sym = ps_sym_lookup("y");
	y = yoffs - sym->e->u.v;
	if (op[1] == 'u') {
		x -= 1;
		y -= 4;
	} else {
		x -= 5;
		y += 4;
	}
	fprintf(fout,
		"<path d=\"M%.2f %.2fl%.2f %.2f\" stroke=\"currentColor\" fill=\"none\"/>\n",
		x, y, dx, -dy);
}

/* arp - ltr */
static void arp_ltr(char type)
{
	float x, y, t;
	int n;

	def_use(D_ltr);
	y = yoffs - pop_free_val();
	x = xoffs + pop_free_val();
	n = (pop_free_val() + 5) / 6;
	if (type == 'a') {
		fprintf(fout, "<g transform=\"rotate(270)\">\n");
		t = x;
		x = -y;
		y = t;
	}
	y -= 4;
	while (--n >= 0) {
		fprintf(fout, "<use x=\"%.2f\" y=\"%.2f\" xlink:href=\"#ltr\"/>\n",
			x, y);
			x += 6;
	}
	if (type == 'a')
		fprintf(fout, "</g>\n");
}

/* sd su gd gu */
static void stem(char *op)
{
	struct ps_sym_s *sym;
	float x, y, dx, h;

	gcur.linewidth = DLW;
	setg(1);
	h = pop_free_val();
	if (op[0] == 's')
		dx = 3.5;
	else
		dx = 1.6;
	if (op[1] == 'd')
		dx = -dx;
	sym = ps_sym_lookup("x");
	x = xoffs + sym->e->u.v + dx;
	sym = ps_sym_lookup("y");
	y = yoffs - sym->e->u.v;

	fprintf(fout,
		"<path d=\"M%.2f %.2fv%.2f\" stroke=\"currentColor\" fill=\"none\"/>\n",
		x, y, -h);
}

/* output a xml string */
static void xml_str_out(char *p)
{
	char *q, *r;

	q = p;
	for (q = p; *p != '\0';) {
		switch (*p++) {
		case '<': r = "&lt;"; break;
		case '>': r = "&gt;"; break;
		case '\'': r = "&apos;"; break;
		case '"': r = "&quot;"; break;
		case '&':
			if (*p == '#')
				continue;
			r = "&amp;";
			break;
		default:
			continue;
		}
		if (p - 1 != q)
			fwrite(q, 1, p - 1 - q, fout);
		q = p;
		fputs(r, fout);
	}
	if (p != q)
		fputs(q, fout);
}

/*
 * types:
 *	s show / c showc / r showr / j jshow / b showb /x gxshow
 */
static void show(char type)
{
	float x, y, w;
	char tmp[4], *s, *p, *q;
	int span;

	span = 0;
	if (memcmp(&gcur, &gold, sizeof gcur) != 0) {
		if (g == 2)
			span = 1;
		else
			defg1();
	}
	x = cx;
	y = cy;
	switch (type) {
	case 'j':
		w = pop_free_val();
		p = tmp;
		tmp[0] = '\0';
		s = NULL;
		break;
	default:
		if (stack->type == STR) {
			s = pop_free_str();
			if (!s || s[0] != '(') {
				fprintf(stderr, "svg: No string\n");
				ps_error = 1;
				return;
			}
			p = s + 1;			/* remove '(' */
		} else {
			p = tmp;
			tmp[0] = pop_free_val();
			tmp[1] = '\0';
			s = NULL;
		}
		w = strw(p);
		if (type == 'x') {		/* gxshow */
			w = pop_free_val();	/* inter TAB width */
			q = strchr(p, '\t');
			*q = '\0';		/* string after the 1st one */
		}
		break;
	}
	if (span) {
		fprintf(fout,"<tspan\n\t");
		selfont(1);
		fprintf(fout,">");
	} else if (g != 2) {
		fprintf(fout, "<text x=\"%.2f\" y=\"%.2f\"", x + xoffs, yoffs - y);
		switch (type) {
		case 'c':
			fprintf(fout, " text-anchor=\"middle\"");
			w /= 2;
			break;
		case 'r':
			fprintf(fout, " text-anchor=\"end\"");
			w = 0;
			break;
		case 'j':
			fprintf(fout, " textLength=\"%.2f\"", w);
			break;
		}

//		if (gcur.rgb != 0)
//			fprintf(fout, " fill=\"currentColor\"");
		fputs(">", fout);
		g = 2;
	}

back:
	xml_str_out(p);
	if (span)
		fprintf(fout, "</tspan>");

	if (type == 'x') {
		p = p + strlen(p) + 1;		/* next string of gxshow */
		q = strchr(p, '\t');
		if (q) {
			*q = '\0';
		} else {

			/* restore the string width (!! tied to elt_free() !!) */
			w = free_elt->u.v;
			type = 's';
		}
		fprintf(fout, "<tspan dx=\"%.2f\">", w);
		span = 1;
		goto back;
	}
	if (type == 'b') {
		setg(1);
		fprintf(fout,
			"<rect stroke=\"currentColor\" fill=\"none\" stroke-width=\"0.6\"\n"
			"	x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\"/>\n",
			xoffs + cx - 2, yoffs - y - gcur.font_s + 2, w + 4, gcur.font_s + 1);
	}
	cx = x + w;
	if (s)
		free(s);
}

static void ps_exec(char *op);

/* execute a sequence
 * returns 1 on 'exit' or error */
static int seq_exec(struct elt_s *e)
{
	struct elt_s *e2;

	switch (e->type) {
	case STR:
		if (e->u.s[0] != '/'
		 && e->u.s[0] != '(') {
			if (strcmp(e->u.s, "exit") == 0)
				return 1;
			ps_exec(e->u.s);
			return 0;
		}
		/* fall thru */
	case VAL:
	case BRK:
		e = elt_dup(e);
		if (!e)
			return 1;
		push(e);
		return 0;
	}
	/* (e->type == SEQ) */
	e = e->u.e;
	while (e) {
		switch (e->type) {
		case STR:
			if (strcmp(e->u.s, "exit") == 0)
				return 1;
			if (e->u.s[0] != '(' && e->u.s[0] != '/') {
				ps_exec(e->u.s);
				break;
			}
			/* fall thru */
		default:
			e2 = elt_dup(e);
			if (!e2)
				return 1;
			push(e2);
			break;
		}
		e = e->next;
	}
	return 0;
}

/* execute a command */
/* (in case of error, a string may be not freed, but it is not important!) */
static void ps_exec(char *op)
{
	struct ps_sym_s *sym;
	struct elt_s *e, *e2;
	float x, y, w, h;
	int n;
	char *s;

	if (ps_error)
		return;
#if 0
fprintf(stderr, "%s ", op);
stack_dump();
#endif
	sym = ps_sym_lookup(op);
	if (sym) {
		sym->exec++;
		if (sym->exec > 2) {
			fprintf(stderr, "svg: Too many recursions\n");
			ps_error = 1;
			return;
		}
		seq_exec(sym->e);
		sym->exec--;
		return;
	}

	if (*op == ' ')				/* load */
		op++;

	switch (*op) {
	case '!':				/* def */
		if (op[1] == '\0') {
			if (!stack) {
				fprintf(stderr, "svg def: Stack empty\n");
				ps_error = 1;
				return;
			}
			e = pop(stack->type);	/* value */
			s = pop_free_str();	/* symbol */
			if (!s || *s != '/') {
				fprintf(stderr, "svg def: No / bad symbol\n");
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
			ps_sym_def(&s[1], e);
			free(s);
			return;
		}
		break;
	case 'a':
		if (strcmp(op, "accent") == 0) {
			xysym(op, D_accent);
			return;
		}
		if (strcmp(op, "abs") == 0) {
			if (!stack || stack->type != VAL) {
				fprintf(stderr, "svg abs: Bad value\n");
				ps_error = 1;
				return;
			}
			if (stack->u.v < 0)
				stack->u.v = -stack->u.v;
			return;
		}
		if (strcmp(op, "add") == 0) {
			x = pop_free_val();
			if (!stack || stack->type != VAL) {
				fprintf(stderr, "svg add: Bad value\n");
				ps_error = 1;
				return;
			}
			stack->u.v += x;
			return;
		}
		if (strcmp(op, "and") == 0) {
			x = pop_free_val();
			if (!stack || stack->type != VAL) {
				fprintf(stderr, "svg and: Bad value\n");
				ps_error = 1;
				return;
			}
			stack->u.v = (int) x & (int) stack->u.v;
			return;
		}
		if (strcmp(op, "anshow") == 0) {
			show('s');
			return;
		}
		if (strcmp(op, "arc") == 0
		 || strcmp(op, "arcn") == 0) {
			float r, a1, a2, x1, y1, x2, y2;

			path_def();
			a2 = pop_free_val();
			a1 = pop_free_val();
			r = pop_free_val();
			if (r < 0) {
				fprintf(stderr, "svg arc: Bad value\n");
				ps_error = 1;
				return;
			}
			y = pop_free_val();
			x = pop_free_val();
			x1 = x + r * cos(a1 * M_PI / 180);
			y1 = y + r * sinf(a1 * M_PI / 180);
			if (a1 >= 360)
				a1 -= 360;
			if (a2 >= 360)
				a2 -= 360;
			path_print("\t", fout);
			if (x1 != cx || y1 != cy)
				path_print("m%.2f %.2f", 
					x1 - cx, -(y1 - cy));
			if (a1 == a2) {			/* circle */
				a2 = 180 - a1;
				x2 = x + r * cosf(a2 * M_PI / 180);
				y2 = y + r * sinf(a2 * M_PI / 180);
				path_print("a%.2f %.2f 0 0 %d %.2f %.2f"
						"a%.2f %.2f 0 0 %d %.2f %.2f\n",
					r, r, op[3] == 'n', x2 - x1, -(y2 - y1),
					r, r, op[3] == 'n', x1 - x2, -(y1 - y2));
				cx = x1;
				cy = y1;
			} else {
				x2 = x + r * cosf(a2 * M_PI / 180);
				y2 = y + r * sinf(a2 * M_PI / 180);
				path_print("a%.2f %.2f 0 0 %d %.2f %.2f\n",
					r, r, op[3] == 'n', x2 - x1, -(y2 - y1));
				cx = x2;
				cy = y2;
			}
			return;
		}
		if (strcmp(op, "arp") == 0) {
			arp_ltr('a');
			return;
		}
		if (strcmp(op, "atan") == 0) {
			y = pop_free_val();	/* den */
			if (!stack || stack->type != VAL) {
				fprintf(stderr, "svg atan: Bad value\n");
				ps_error = 1;
				return;
			}
			x = stack->u.v;		/* num */
			if (x == 0 && y == 0) {
				fprintf(stderr, "svg atan: Bad value\n");
				ps_error = 1;
				return;
			}
			stack->u.v = atan(x / y) / M_PI * 180;
			return;
		}
		break;
	case 'b':
		if (strcmp(op, "bar") == 0) {
			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			h = pop_free_val();
			fprintf(fout, "<path stroke=\"currentColor\" fill=\"none\"\n"
				"	d=\"M%.2f %.2fv%.2f\"/>\n",
				x, y, -h);
			return;
		}
		if (strcmp(op, "bclef") == 0) {
			xysym(op, D_bclef);
			return;
		}
		if (strcmp(op, "bdef") == 0) {
			ps_exec("!");
			return;
		}
		if (strcmp(op, "bind") == 0) {
			return;
		}
		if (strcmp(op, "bitshift") == 0) {
			int shift;

			shift = pop_free_val();
			if (!stack || stack->type != VAL
			 || shift >= 32  || shift < -32) {
				fprintf(stderr, "svg: Bad value for bitshift\n");
				ps_error = 1;
				return;
			}
			if (shift > 0)
				n = (int) stack->u.v << shift;
			else
				n = (int) stack->u.v >> -shift;
			stack->u.v = n;
			return;
		}
		if (strcmp(op, "bm") == 0) {
			float dx, dy;

			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			dy = pop_free_val();
			dx = pop_free_val();
			h = pop_free_val();
			fprintf(fout,
				"<path fill=\"currentColor\"\n"
				"	d=\"M%.2f %.2fl%.2f %.2fv%.2fl%.2f %.2f\"/>\n",
				x, y, dx, -dy, h,-dx, dy);
			return;
		}
		if (strcmp(op, "bnum") == 0
		 || strcmp(op, "bnumb") == 0) {
			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			s = pop_free_str();
			if (!s) {
				fprintf(stderr, "svg: No string\n");
				ps_error = 1;
				return;
			}
			if (op[4] == 'b') {
				w = 7 * strlen(s);
				fprintf(fout,
					"<rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"12\" fill=\"white\"/>\n",
					x - w / 2, y - 10, w);
			}
			fprintf(fout,
				"<text font-family=\"serif\" font-size=\"12\" font-style=\"italic\" font-weight=\"normal\"\n"
				"	x=\"%.2f\" y=\"%.2f\" text-anchor=\"middle\">%s</text>\n",
				x, y, s + 1);
			free(s);
			return;
		}
		if (strcmp(op, "box") == 0) {
			setg(1);
			h = pop_free_val();
			w = pop_free_val();
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			fprintf(fout,
				"<rect stroke=\"currentColor\" fill=\"none\"\n"
				"	x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\"/>\n",
				x, y - h, w, h);
			return;
		}
		if (strcmp(op, "boxdraw") == 0) {
			setg(1);
			h = pop_free_val();
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			fprintf(fout,
				"<rect stroke=\"currentColor\" fill=\"none\"\n"
				"	x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\"/>\n",
				x, y - h, boxend - (x - xoffs) + 2, h);
			return;
		}
		if (strcmp(op, "boxmark") == 0) {
			if (cx > boxend)
				boxend = cx;
			return;
		}
		if (strcmp(op, "boxstart") == 0) {
			boxend = cx;
			return;
		}
		if (strcmp(op, "brace") == 0) {
			def_use(D_brace);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			h = pop_free_val() * 0.01;
			fprintf(fout,
				"<g transform=\"translate(%.2f,%.2f) scale(1,%.2f)\">\n"
				"	<use xlink:href=\"#brace\"/>\n"
				"</g>\n",
				x, y, h);
			return;
		}
		if (strcmp(op, "bracket") == 0) {
			setg(1);
			y = yoffs - pop_free_val() - 3;
			x = xoffs + pop_free_val() - 5;
			h = pop_free_val() + 2;
			fprintf(fout,
				"<path fill=\"currentColor\"\n"
				"	d=\"M%.2f %.2f\n"
				"	c10.5 1 12 -4.5 12 -3.5c0 1 -3.5 5.5 -8.5 5.5\n"
				"	v%.2f\n"
				"	c5 0 8.5 4.5 8.5 5.5c0 1 -1.5 -4.5 -12 -3.5\"/>\n",
				x, y, h);
			return;
		}
		if (strcmp(op, "breve") == 0) {
			setxysym(op, D_breve);
			return;
		}
		if (strcmp(op, "brth") == 0) {
			setg(1);
			y = yoffs - pop_free_val() - 6;
			x = xoffs + pop_free_val();
			fprintf(fout, "<text x=\"%.2f\" y=\"%.2f\" font-family=\"serif\" font-size=\"30\"\n"
				"	font-weight=\"bold\" font-style=\"italic\">,</text>\n",
				x, y);
			return;
		}
		break;
	case 'C':
		if (strcmp(op, "C") == 0) {
			float c1, c2, c3, c4;

curveto:
			path_def();
			y = pop_free_val();
			x = pop_free_val();
			c4 = yoffs - pop_free_val();
			c3 = xoffs + pop_free_val();
			c2 = yoffs - pop_free_val();
			c1 = xoffs + pop_free_val();
			path_print("\tC%.2f %.2f %.2f %.2f %.2f %.2f\n",
				c1, c2, c3, c4, xoffs + x, yoffs - y);
			cx = x;
			cy = y;
			return;
		}
		break;
	case 'c':
		if (strcmp(op, "cclef") == 0) {
			xysym(op, D_cclef);
			return;
		}
		if (strcmp(op, "csig") == 0) {
			xysym(op, D_csig);
			return;
		}
		if (strcmp(op, "ctsig") == 0) {
			xysym(op, D_ctsig);
			return;
		}
		if (strcmp(op, "coda") == 0) {
			xysym(op, D_coda);
			return;
		}
		if (strcmp(op, "closepath") == 0) {
			path_def();
			path_print("\tz");
			return;
		}
		if (strcmp(op, "composefont") == 0) {
			pop(BRK);
			pop(STR);
			return;
		}
		if (strcmp(op, "copy") == 0) {
			struct elt_s *e3;

			n = pop_free_val();
			if ((unsigned) n > 10) {
				fprintf(stderr, "svg copy: Too wide\n");
				ps_error = 1;
				return;
			}
			e = stack;
			e2 = NULL;
			while (--n >= 0) {
				if (!e)
					break;
				e3 = elt_dup(e);
				if (!e3)
					return;
				e3->next = e2;
				e2 = e3;
				e = e->next;
			}
			if (n >= 0) {
				fprintf(stderr, "svg copy: Stack empty\n");
				ps_error = 1;
				return;
			}
			while (e2) {
				e3 = e2->next;
				push(e2);
				e2 = e3;
			}
			return;
		}
		if (strcmp(op, "cos") == 0) {
			if (!stack || stack->type != VAL) {
				fprintf(stderr, "svg cos: Bad value\n");
				ps_error = 1;
				return;
			}
			stack->u.v = cos(stack->u.v * M_PI / 180);
			return;
		}
		if (strcmp(op, "cpu") == 0) {
			xysym(op, D_cpu);
			return;
		}
		if (strcmp(op, "crdc") == 0) {
			setg(1);
			y = yoffs - pop_free_val() - 5;
			x = xoffs + pop_free_val();
			s = pop_free_str();
			if (!s) {
				fprintf(stderr, "svg crdc: No string\n");
				ps_error = 1;
				return;
			}
			fprintf(fout, "<text font-family=\"serif\" font-size=\"16\" font-weight=\"normal\" font-style=\"italic\"\n"
				"	x=\"%.2f\" y=\"%.2f\" text-anchor=\"left\">%s</text>\n",
				x, y, s + 1);
			free(s);
			return;
		}
		if (strcmp(op, "cresc") == 0) {
			setg(1);
			y = yoffs - pop_free_val() - 5;
			x = xoffs + pop_free_val();
			w = pop_free_val();
			sym = ps_sym_lookup("defl");
			x += w;
			if ((int) sym->e->u.v & 1)
				fprintf(fout, "<path stroke=\"currentColor\" fill=\"none\"\n"
					"d=\"M%.2f %.2fl%.2f -2.2m0 -3.6l%.2f -2.2\"/>\n",
					x, y, -w, w);
			else
				fprintf(fout, "<path stroke=\"currentColor\" fill=\"none\"\n"
					"d=\"M%.2f %.2fl%.2f -4l%.2f -4\"/>\n",
					x, y, -w, w);
			return;
		}
		if (strcmp(op, "custos") == 0) {
			xysym(op, D_custos);
			return;
		}
		if (strcmp(op, "currentgray") == 0) {
			e = elt_new();
			if (!e)
				return;
			e->type = VAL;
			e->u.v = (float) gcur.rgb / 0xffffff;
			push(e);
			return;
		}
		if (strcmp(op, "currentpoint") == 0) {
			e = elt_new();
			if (!e)
				return;
			e->type = VAL;
			e->u.v = cx;
			push(e);
			e = elt_new();
			if (!e)
				return;
			e->type = VAL;
			e->u.v = cy;
			push(e);
			return;
		}
		if (strcmp(op, "curveto") == 0)
			goto curveto;
		if (strcmp(op, "cvi") == 0) {
			if (!stack || stack->type != VAL) {
				fprintf(stderr, "svg cvi: Bad value\n");
				ps_error = 1;
				return;
			}
			n = stack->u.v;
			stack->u.v = n;
			return;
		}
		if (strcmp(op, "cvx") == 0) {
			s = pop_free_str();
			if (!s || ((*s != '/') && (*s != '('))) {
				fprintf(stderr, "svg cvx: No / bad string\n");
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
			*s = '{';
			svg_write(s, strlen(s));
			svg_write("}", 1);
			free(s);
			return;
		}
		break;
	case 'd':
		if (strcmp(op, "dacs") == 0) {
			setg(1);
			y = yoffs - pop_free_val() - 3;
			x = xoffs + pop_free_val();
			s = pop_free_str();
			if (!s) {
				fprintf(stderr, "svg dacs: No string\n");
				ps_error = 1;
				return;
			}
			fprintf(fout, "<text font-family=\"serif\" font-size=\"16\" font-weight=\"normal\" font-style=\"normal\"\n"
				"	x=\"%.2f\" y=\"%.2f\" text-anchor=\"middle\">%s</text>\n",
				x, y, s + 1);
			free(s);
			return;
		}
		if (strcmp(op, "def") == 0) {
			ps_exec("!");
			return;
		}
		if (strcmp(op, "dim") == 0) {
			setg(1);
			y = yoffs - pop_free_val() - 5;
			x = xoffs + pop_free_val();
			w = pop_free_val();
			sym = ps_sym_lookup("defl");
			if ((int) sym->e->u.v & 2)
				fprintf(fout, "<path stroke=\"currentColor\" fill=\"none\"\n"
					"d=\"M%.2f %.2fl%.2f -2.2m0 -3.6l%.2f -2.2\"/>\n",
					x, y, w, -w);
			else
				fprintf(fout, "<path stroke=\"currentColor\" fill=\"none\"\n"
					"d=\"M%.2f %.2fl%.2f -4l%.2f -4\"/>\n",
					x, y, w, -w);
			return;
		}
		if (strcmp(op, "div") == 0) {
			x = pop_free_val();
			if (!stack || stack->type != VAL || x == 0) {
				fprintf(stderr, "svg: Bad value for div\n");
				ps_error = 1;
				return;
			}
			stack->u.v /= x;
			return;
		}
		if (strcmp(op, "dnb") == 0) {
			xysym(op, D_dnb);
			return;
		}
		if (strcmp(op, "dplus") == 0) {
			xysym(op, D_dplus);
			return;
		}
		if (strcmp(op, "dSL") == 0) {
			float a1, a2, a3, a4, a5, a6, m1, m2;

			setg(1);
			m2 = yoffs - pop_free_val();
			m1 = xoffs + pop_free_val();
			a6 = pop_free_val();
			a5 = pop_free_val();
			a4 = pop_free_val();
			a3 = pop_free_val();
			a2 = pop_free_val();
			a1 = pop_free_val();
			fprintf(fout,
				"<path stroke=\"currentColor\" fill=\"none\" stroke-dasharray=\"5,5\"\n"
				"	d=\"M%.2f %.2fc%.2f %.2f %.2f %.2f %.2f %.2f\"/>\n",
					m1, m2, a1, -a2, a3, -a4, a5, -a6);
			return;
		}
		if (strcmp(op, "dt") == 0) {
			setg(1);
			sym = ps_sym_lookup("x");
			x = xoffs + sym->e->u.v;
			sym = ps_sym_lookup("y");
			y = yoffs - sym->e->u.v;
			y -= pop_free_val();
			x += pop_free_val();
			fprintf(fout,
				"<circle fill=\"currentColor\" cx=\"%.2f\" cy=\"%.2f\" r=\"1.2\"/>\n",
				x, y);
			return;
		}
		if (strcmp(op, "dlw") == 0) {
			gcur.linewidth = DLW;
			return;
		}
		if (strcmp(op, "dotbar") == 0) {
			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			h = pop_free_val();
			fprintf(fout,
				"<path stroke=\"currentColor\" fill=\"none\" stroke-dasharray=\"5,5\"\n"
				"	d=\"M%.2f %.2fv%.2f\"/>\n",
				x, y, -h);
			return;
		}
		if (strcmp(op, "dup") == 0) {
			if (!stack) {
				fprintf(stderr, "svg dup: Stack empty\n");
				ps_error = 1;
				return;
			}
			e = elt_dup(stack);
			if (e)
				push(e);
			return;
		}
		if (strcmp(op, "dft0") == 0) {
			xysym(op, D_dft0);
			return;
		}
		if (strcmp(op, "dsh0") == 0) {
			xysym(op, D_dsh0);
			return;
		}
		break;
	case 'e':
		if (strcmp(op, "emb") == 0) {
			xysym(op, D_emb);
			return;
		}
		if (strcmp(op, "eofill") == 0) {
			if (!path) {
				fprintf(stderr, "svg eofill: No path\n");
				ps_error = 1;
				return;
			}
			path_end();
			fprintf(fout, "\t\" fill-rule=\"evenodd\" fill=\"currentColor\"/>\n");
			return;
		}
		if (strcmp(op, "eq") == 0) {
			cond(C_EQ);
			return;
		}
		if (strcmp(op, "exch") == 0) {
			if (!stack || !stack->next) {
				fprintf(stderr, "svg exch: Stack empty\n");
				ps_error = 1;
				return;
			}
			e = stack->next;
			stack->next = e->next;
			e->next = stack;
			stack = e;
			return;
		}
		if (strcmp(op, "exec") == 0) {
			e = pop(SEQ);
			if (!e)
				return;
			seq_exec(e);
			elt_free(e);
			return;
		}
		break;
	case 'F':
		if (sscanf(op, "F%d", &n) == 1) {
			if (strlen(fontnames[n]) >= sizeof gcur.font_n - 1) {
				fprintf(stderr, "svg %s: Font name too long\n",
						op);
			}
			strncpy(gcur.font_n, fontnames[n], sizeof gcur.font_n);
			gcur.font_n[sizeof gcur.font_n - 1] = '\0';
			gcur.font_s = pop_free_val();
			return;
		}
		break;
	case 'f':
		if (strcmp(op, "false") == 0) {
			e = elt_new();
			if (!e)
				return;
			e->type = VAL;
			e->u.v = 0;
			push(e);
			return;
		}
		if (strcmp(op, "fill") == 0) {
			if (!path) {
				fprintf(stderr, "svg fill: No path\n");
//				ps_error = 1;
				return;
			}
			path_end();
			fprintf(fout, "\t\" fill=\"currentColor\"/>\n");
			return;
		}
		if (strcmp(op, "findfont") == 0) {
			s = pop_free_str();
			if (!s
			 || *s != '/'
			 || strlen(s) >= sizeof gcur.font_n) {
				fprintf(stderr, "svg selectfont: No / bad font\n");
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
			strcpy(gcur.font_n, s + 1);
			free(s);
			return;
		}
		if (strcmp(op, "fng") == 0) {
			setg(1);
			y = yoffs - pop_free_val() - 1;
			x = xoffs + pop_free_val() - 3;
			s = pop_free_str();
			if (!s) {
				fprintf(stderr, "svg fng: No string\n");
				ps_error = 1;
				return;
			}
			fprintf(fout, "<text font-family=\"Bookman\" font-size=\"8\" font-weight=\"normal\" font-style=\"normal\"\n"
				"	x=\"%.2f\" y=\"%.2f\">%s</text>\n",
				x, y, s + 1);
			free(s);
			return;
		}
		if (strcmp(op, "for") == 0) {
			float init, incr, limit;

			e = pop(SEQ);			/* proc */
			if (!e)
				return;
			limit = pop_free_val();
			incr = pop_free_val();
			init = pop_free_val();
			if (incr == 0
			 || (limit - init) / incr > 100) {
				fprintf(stderr, "svg for: Bad values\n");
				ps_error = 1;
				return;
			}
			if (incr > 0) {
				while (init <= limit) {
					e2 = elt_new();
					if (!e2)
						break;
					e2->type = VAL;
					e2->u.v = init;
					push(e2);
					if (seq_exec(e) != 0)
						break;
					init += incr;
				}
			} else {
				while (init >= limit) {
					e2 = elt_new();
					if (!e2)
						break;
					e2->type = VAL;
					e2->u.v = init;
					push(e2);
					if (seq_exec(e) != 0)
						break;
					init += incr;
				}
			}
			elt_free(e);
			return;
		}
		if (strcmp(op, "forall") == 0) {
			struct elt_s *e3;
			unsigned char *p;

			e = pop(SEQ);			/* proc */
			if (!e)
				return;
			e2 = stack;			/* array/string */
			if (!e2) {
				fprintf(stderr, "svg forall: Stack empty\n");
				ps_error = 1;
				return;
			}
			stack = e2->next;
			switch (e2->type) {
			case STR:
				p = (unsigned char *) &e2->u.s[1];
				while (*p != '\0') {
					e3 = elt_new();
					if (!e3)
						return;
					e3->u.v = *p++;
					push(e3);
					if (seq_exec(e) != 0)
						break;
				}
				break;
			case BRK:
				for (e3 = e2->u.e; e3; e3 = e3->next) {
					struct elt_s *e4;

					e4 = elt_dup(e3);
					push(e4);
					if (seq_exec(e) != 0)
						break;
				}
				break;
			default:
				fprintf(stderr, "svg forall: Bad any\n");
				ps_error = 1;
				return;
			}
			elt_free(e);
			elt_free(e2);
			return;
		}
		if (strcmp(op, "ft0") == 0) {
			xysym(op, D_ft0);
			return;
		}
		if (strcmp(op, "ft1") == 0) {
			xysym(op, D_ft1);
			return;
		}
		if (strcmp(op, "ft4") == 0) {
			n = pop_free_val();
			switch (n) {
			case 1:
				xysym("ft1", D_ft1);
				break;
			case 2:
				xysym("ft0", D_ft0);
				break;
			case 3:
				xysym("ft513", D_ft513);
				break;
			default:
				xysym("dft0", D_dft0);
				break;
			}
			return;
		}
		if (strcmp(op, "ft513") == 0) {
			xysym(op, D_ft513);
			return;
		}
		break;
	case 'g':
		if (strcmp(op, "gcshow") == 0) {
			show('s');
			return;
		}
		if (strcmp(op, "ge") == 0) {
			cond(C_GE);
			return;
		}
		if (strcmp(op, "get") == 0) {
			n = pop_free_val();
			if (!stack) {
				fprintf(stderr, "svg get: Stack empty\n");
				ps_error = 1;
				return;
			}
			switch (stack->type) {
			case VAL:
				if (n != 0) {
					fprintf(stderr, "svg get: Out of bounds\n");
					ps_error = 1;
					return;
				}
				return;
			case STR:
				s = stack->u.s;
				if (!s || *s != '(') {
					fprintf(stderr, "svg get: Not a string\n");
					if (s)
						free(s);
					ps_error = 1;
					return;
				}
				if ((unsigned) n >= strlen(s) - 1) {
					fprintf(stderr, "svg get: Out of bounds\n");
					ps_error = 1;
					return;
				}
				stack->type = VAL;
				stack->u.v = s[n + 1];
				free(s);
				return;
			}
			e = stack->u.e;
			e2 = NULL;
			while (--n >= 0) {
				if (!e)
					break;
				e2 = e;
				e = e->next;
			}
			if (!e) {
				fprintf(stderr, "svg get: Out of bounds\n");
				ps_error = 1;
				return;
			}
			if (!e2)
				stack->u.e = e->next;
			else
				e2->next = e->next;
			e->next = stack->next;
			elt_free(stack);
			stack = e;
			return;
		}
		if (strcmp(op, "getinterval") == 0) {
			int count;

			count = pop_free_val();
			n = pop_free_val();
			s = pop_free_str();
			if (!s || *s != '(') {
				fprintf(stderr, "svg getinterval: No string\n");
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
			if ((unsigned) n >= strlen(s)
			 || (unsigned) count >= strlen(s) - n) {
				fprintf(stderr, "svg getinterval: Out of bounds\n");
				ps_error = 1;
				return;
			}
			e = elt_new();
			if (!e)
				return;
			e->type = STR;
			e->u.s = malloc(count + 2);
			e->u.s[0] = '(';
			memcpy(&e->u.s[1], &s[n + 1], count);
			e->u.s[count + 1] = '\0';
			push(e);
			free(s);
			return;
		}
		if (strcmp(op, "ghd") == 0) {
			setxysym(op, D_ghd);
			return;
		}
		if (strcmp(op, "ghl") == 0) {
			xysym(op, D_ghl);
			return;
		}
		if (strcmp(op, "gt") == 0) {
			cond(C_GT);
			return;
		}
		if (strcmp(op, "gu") == 0
		 || strcmp(op, "gd") == 0) {
			stem(op);
			return;
		}
		if (strcmp(op, "gua") == 0
		 || strcmp(op, "gda") == 0) {
			acciac(op);
			return;
		}
		if (strcmp(op, "grestore") == 0) {
			if (nsave <= 0) {
				fprintf(stderr, "svg grestore: No gsave\n");
				ps_error = 1;
				return;
			}
			setg(1);
			nsave--;
			cx = gsave[nsave].cx;
			cy = gsave[nsave].cy;
			xoffs = gsave[nsave].xoffs;
			yoffs = gsave[nsave].yoffs;
			x_rot = gsave[nsave].x_rot;
			y_rot = gsave[nsave].y_rot;
			memcpy(&gcur, &gsave[nsave].gc, sizeof gcur);
			return;
		}
		if (strcmp(op, "grm") == 0) {
			xysym(op, D_grm);
			return;
		}
		if (strcmp(op, "gsave") == 0) {
			if (nsave >= (int) (sizeof gsave / sizeof gsave[0])) {
				fprintf(stderr, "svg grestore: Too many gsave's\n");
				ps_error = 1;
				return;
			}
			setg(1);
			memcpy(&gsave[nsave].gc, &gcur, sizeof gsave[0].gc);
			gsave[nsave].cx = cx;
			gsave[nsave].cy = cy;
			gsave[nsave].xoffs = xoffs;
			gsave[nsave].yoffs = yoffs;
			gsave[nsave].x_rot = x_rot;
			gsave[nsave].y_rot = y_rot;
			nsave++;
			return;
		}
		if (strcmp(op, "gsl") == 0) {
			float a1, a2, a3, a4, a5, a6, m1, m2;

			setg(1);
			m2 = yoffs - pop_free_val();
			m1 = xoffs + pop_free_val();
			a6 = pop_free_val();
			a5 = pop_free_val();
			a4 = pop_free_val();
			a3 = pop_free_val();
			a2 = pop_free_val();
			a1 = pop_free_val();
			fprintf(fout,
				"<path stroke=\"currentColor\" fill=\"none\"\n"
				"	d=\"M%.2f %.2fc%.2f %.2f %.2f %.2f %.2f %.2f\"/>\n",
					m1, m2, a1, -a2, a3, -a4, a5, -a6);
			return;
		}
		if (strcmp(op, "gxshow") == 0) {
			show('x');
			return;
		}
		break;
	case 'H':
		if (strcmp(op, "Hd") == 0) {
			setxysym(op, D_Hd);
			return;
		}
		if (strcmp(op, "HD") == 0) {
			setxysym(op, D_HD);
			return;
		}
		if (strcmp(op, "HDD") == 0) {
			setxysym(op, D_HDD);
			return;
		}
		break;
	case 'h':
		if (strcmp(op, "hd") == 0) {
			setxysym(op, D_hd);
			return;
		}
		if (strcmp(op, "hl") == 0) {
			xysym(op, D_hl);
			return;
		}
		if (strcmp(op, "hl1") == 0) {
			xysym(op, D_hl1);
			return;
		}
		if (strcmp(op, "hl2") == 0) {
			xysym(op, D_hl2);
			return;
		}
		if (strcmp(op, "hld") == 0) {
			xysym(op, D_hld);
			return;
		}
		if (strcmp(op, "hyph") == 0) {
			int d;

			setg(1);
			y = pop_free_val();
			x = pop_free_val();
			w = pop_free_val();
			d = 25 + (int) w / 20 * 3;
			n = (w - 15.) / d;
			x += (w - d * n - 5) / 2;
			fprintf(fout, "<path stroke=\"currentColor\" fill=\"none\" stroke-width=\"1.2\"\n"
				"	stroke-dasharray=\"5,%d\"\n"
				"	d=\"M%.2f %.2fh%d\"/>\n",
				d - 5,
				xoffs + x, yoffs - y - gcur.font_s * 0.3, d * n + 5);
			return;
		}
		break;
	case 'i':
		if (strcmp(op, "idiv") == 0) {
			n = pop_free_val();
			if (!stack || stack->type != VAL || n == 0) {
				fprintf(stderr, "svg idiv: Bad value\n");
				ps_error = 1;
				return;
			}
			n = (int) stack->u.v / n;
			stack->u.v = n;
			return;
		}
		if (strcmp(op, "if") == 0) {
			e = pop(SEQ);		/* sequence */
			if (!e)
				return;
			n = pop_free_val();	/* condition */
			if (n != 0)
				seq_exec(e);
			elt_free(e);
			return;
		}
		if (strcmp(op, "ifelse") == 0) {
			e2 = pop(SEQ);		/* sequence 2 */
			e = pop(SEQ);		/* sequence 1 */
			if (!e || !e2)
				return;
			n = pop_free_val();	/* condition */
			if (n != 0)
				seq_exec(e);
			else
				seq_exec(e2);
			elt_free(e);
			elt_free(e2);
			return;
		}
		if (strcmp(op, "imsig") == 0) {
			xysym(op, D_imsig);
			return;
		}
		if (strcmp(op, "iMsig") == 0) {
			xysym(op, D_iMsig);
			return;
		}
		if (strcmp(op, "index") == 0) {
			n = pop_free_val();
			e = stack;
			while (--n >= 0) {
				if (!e)
					break;
				e = e->next;
			}
			if (!e) {
				fprintf(stderr, "svg index: Stack empty\n");
				ps_error = 1;
				return;
			}
			e = elt_dup(e);
			if (!e)
				return;
			push(e);
			return;
		}
		break;
	case 'j':
		if (strcmp(op, "jshow") == 0) {
			show('j');
			return;
		}
		break;
	case 'L':
		if (strcmp(op, "L") == 0) {
lineto:
			path_def();
			y = pop_free_val();
			x = pop_free_val();
			if (x == cx)
				path_print("\tv%.2f\n", cy - y);
			else if (y == cy)
				path_print("\th%.2f\n", x - cx);
			else
				path_print("\tl%.2f %.2f\n",
					x - cx, cy - y);
			cx = x;
			cy = y;
			return;
		}
	case 'l':
		if (strcmp(op, "le") == 0) {
			cond(C_LE);
			return;
		}
		if (strcmp(op, "lt") == 0) {
			cond(C_LT);
			return;
		}
		if (strcmp(op, "length") == 0) {
			s = pop_free_str();
			if (!s || *s != '(') {
				fprintf(stderr, "svg length: No string\n");
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
			e = elt_new();
			if (!e)
				return;
			e->type = VAL;
			e->u.v = strlen(s + 1);
			push(e);
			free(s);
			return;
		}
		if (strcmp(op, "lineto") == 0)
			goto lineto;
		if (strcmp(op, "lmrd") == 0) {
			xysym(op, D_lmrd);
			return;
		}
		if (strcmp(op, "load") == 0) {
			s = pop_free_str();
			if (!s || *s != '/') {
				fprintf(stderr, "svg load: No / bad symbol\n");
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
			sym = ps_sym_lookup(s + 1);
			if (!sym) {
				e = elt_new();
				if (!e)
					return;
				e->type = STR;
				e->u.s = strdup(s);
				e->u.s[0] = ' ';	/* internal */
			} else {
				e = elt_dup(sym->e);
				if (!e)
					return;
			}
			free(s);
			push(e);
			return;
		}
		if (strcmp(op, "longa") == 0) {
			setxysym(op, D_longa);
			return;
		}
		if (strcmp(op, "lphr") == 0) {
			xysym(op, D_lphr);
			return;
		}
		if (strcmp(op, "ltr") == 0) {
			arp_ltr('l');
			return;
		}
		if (strcmp(op, "lyshow") == 0) {
			show('s');
			return;
		}
		break;
	case 'M':
		if (strcmp(op, "M") == 0) {
moveto:
			cy = pop_free_val();
			cx = pop_free_val();
			if (path) {
				path_print("\tM%.2f %.2f\n", xoffs + cx, yoffs - cy);
			} else if (g == 2) {
				fputs("</text>\n", fout);
				g = 1;
			}
			return;
		}
		break;
	case 'm':
		if (strcmp(op, "marcato") == 0) {
			xysym(op, D_marcato);
			return;
		}
		if (strcmp(op, "moveto") == 0)
			goto moveto;
		if (strcmp(op, "mphr") == 0) {
			xysym(op, D_mphr);
			return;
		}
		if (strcmp(op, "mod") == 0) {
			x = pop_free_val();
			if (!stack || stack->type != VAL || x == 0) {
				fprintf(stderr, "svg: Bad value for mod\n");
				ps_error = 1;
				return;
			}
			n = (int) stack->u.v % (int) x;
			stack->u.v = n;
			return;
		}
		if (strcmp(op, "mrep") == 0) {
			xysym(op, D_mrep);
			return;
		}
		if (strcmp(op, "mrep2") == 0) {
			xysym(op, D_mrep2);
			return;
		}
		if (strcmp(op, "mrest") == 0) {
			def_use(D_mrest);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			s = pop_free_str();
			if (!s) {
				fprintf(stderr, "svg: No string\n");
				ps_error = 1;
				return;
			}
			fprintf(fout, "<use x=\"%.2f\" y=\"%.2f\" xlink:href=\"#mrest\"/>\n"
				"<text font-family=\"serif\" font-size=\"15\" font-weight=\"bold\" font-style=\"normal\"\n"
				"	x=\"%.2f\" y=\"%.2f\" text-anchor=\"middle\">%s</text>\n",
				x, y, x, y - 28, s + 1);
			free(s);
			return;
		}
		if (strcmp(op, "mul") == 0) {
			x = pop_free_val();
			if (!stack || stack->type != VAL) {
				fprintf(stderr, "svg: Bad value for mul\n");
				ps_error = 1;
				return;
			}
			stack->u.v *= x;
			return;
		}
		break;
	case 'n':
		if (strcmp(op, "ne") == 0) {
			cond(C_NE);
			return;
		}
		if (strcmp(op, "neg") == 0) {
			if (!stack || stack->type != VAL) {
				fprintf(stderr, "svg: Bad value for neg\n");
				ps_error = 1;
				return;
			}
			stack->u.v = -stack->u.v;
			return;
		}
		if (strcmp(op, "newpath") == 0) {
			path_def();
			return;
		}
		if (strcmp(op, "nt0") == 0) {
			xysym(op, D_nt0);
			return;
		}
		break;
	case 'o':
		if (strcmp(op, "octl") == 0
		 || strcmp(op, "octu") == 0) {
			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			if (op[3] == 'l')
				x -= 3.5;
			else
				x -= 2.5;
			fprintf(fout, "<text font-family=\"serif\" font-size=\"12\" font-weight=\"normal\" font-style=\"normal\"\n"
				"	x=\"%.2f\" y=\"%.2f\">8</text>\n",
				x, y);
			return;
		}
		if (strcmp(op, "opend") == 0) {
			xysym(op, D_opend);
			return;
		}
		if (strcmp(op, "or") == 0) {
			x = pop_free_val();
			if (!stack || stack->type != VAL) {
				fprintf(stderr, "svg or: Bad value\n");
				ps_error = 1;
				return;
			}
			stack->u.v = (int) x & (int) stack->u.v;
			return;
		}
		break;
	case 'p':
		if (strcmp(op, "pclef") == 0) {
			def_use(D_pclef);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			fprintf(fout, "<use x=\"%.2f\" y=\"%.2f\" xlink:href=\"#%s\"/>\n",
				x, y, op);
			return;
		}
		if (strcmp(op, "pf") == 0) {
			setg(1);
			y = yoffs - pop_free_val() - 5;
			x = xoffs + pop_free_val();
			s = pop_free_str();
			if (!s) {
				fprintf(stderr, "svg pf: No string\n");
				ps_error = 1;
				return;
			}
			fprintf(fout, "<text font-family=\"serif\" font-size=\"16\" font-weight=\"bold\" font-style=\"italic\"\n"
				"	x=\"%.2f\" y=\"%.2f\">%s</text>\n",
				x, y, s + 1);
			free(s);
			return;
		}
		if (strcmp(op, "pmsig") == 0) {
			xysym(op, D_pmsig);
			return;
		}
		if (strcmp(op, "pMsig") == 0) {
			xysym(op, D_pMsig);
			return;
		}
		if (strcmp(op, "pop") == 0) {
			if (!stack) {
				fprintf(stderr, "svg pop: Stack empty\n");
				ps_error = 1;
				return;
			}
			e = pop(stack->type);
			elt_free(e);
			return;
		}
		if (strcmp(op, "pshhd") == 0) {
			setxysym(op, D_pshhd);
			return;
		}
		if (strcmp(op, "pdshhd") == 0) {
			setxysym("pshhd", D_pshhd);
			return;
		}
		if (strcmp(op, "pfthd") == 0) {
			setxysym(op, D_pfthd);
			return;
		}
		if (strcmp(op, "pdfthd") == 0) {
			setxysym("pfthd", D_pfthd);
			return;
		}
#if 0
//fixme: cannot work because duplication...
		if (strcmp(op, "put") == 0) {
			int v;

			v = pop_free_val();
			n = pop_free_val();
			if (!stack) {
				fprintf(stderr, "svg put: Stack empty\n");
				ps_error = 1;
				return;
			}
			s = pop_free_str();
			if (!s || *s != '(') {
				fprintf(stderr, "svg put: No string\n");
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
			if ((unsigned) n >= strlen(s) - 1) {
				fprintf(stderr, "svg put: Out of bounds\n");
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
//fixme: should keep the original string...
			s[n + 1] = v;
			free(s);
			return;
		}
#endif
		break;
	case 'R':
		if (strcmp(op, "RC") == 0) {
			float c1, c2, c3, c4;

rcurveto:
			path_def();
			y = pop_free_val();
			x = pop_free_val();
			c4 = pop_free_val();
			c3 = pop_free_val();
			c2 = pop_free_val();
			c1 = pop_free_val();
			path_print("\tc%.2f %.2f %.2f %.2f %.2f %.2f\n",
				c1, -c2, c3, -c4, x, -y);
			cx += x;
			cy += y;
			return;
		}
		if (strcmp(op, "RL") == 0) {
rlineto:
			path_def();
			y = pop_free_val();
			x = pop_free_val();
			if (x == 0)
				path_print("\tv%.2f\n", -y);
			else if (y == 0)
				path_print("\th%.2f\n", x);
			else
				path_print("\tl%.2f %.2f\n", x, -y);
			cx += x;
			cy += y;
			return;
		}
		if (strcmp(op, "RM") == 0) {
rmoveto:
			y = pop_free_val();
			x = pop_free_val();
			if (path) {
				path_print("\tm%.2f %.2f\n", x, -y);
			} else if (g == 2) {
				fputs("</text>\n", fout);
				g = 1;
			}
			cx += x;
			cy += y;
			return;
		}
		break;
	case 'r':
		if (strcmp(op, "r00") == 0) {
			setxysym(op, D_r00);
			return;
		}
		if (strcmp(op, "r0") == 0) {
			setxysym(op, D_r0);
			return;
		}
		if (strcmp(op, "r1") == 0) {
			setxysym(op, D_r1);
			return;
		}
		if (strcmp(op, "r2") == 0) {
			setxysym(op, D_r2);
			return;
		}
		if (strcmp(op, "r4") == 0) {
			setxysym(op, D_r4);
			return;
		}
		if (strcmp(op, "r8") == 0) {
			setxysym(op, D_r8);
			return;
		}
		if (strcmp(op, "r16") == 0) {
			setxysym(op, D_r16);
			return;
		}
		if (strcmp(op, "r32") == 0) {
			setxysym(op, D_r32);
			return;
		}
		if (strcmp(op, "r64") == 0) {
			setxysym(op, D_r64);
			return;
		}
		if (strcmp(op, "r128") == 0) {
			setxysym(op, D_r128);
			return;
		}
		if (strcmp(op, "rdots") == 0) {
			xysym(op, D_rdots);
			return;
		}
		if (strcmp(op, "rcurveto") == 0)
			goto rcurveto;
		if (strcmp(op, "rlineto") == 0)
			goto rlineto;
		if (strcmp(op, "rmoveto") == 0)
			goto rmoveto;
		if (strcmp(op, "roll") == 0) {
			int i, j;

			j = pop_free_val();
			n = pop_free_val();
			if (n <= 0) {
				fprintf(stderr, "svg roll: Invalid value\n");
				ps_error = 1;
				return;
			}
			if (j > 0) {
				j = j % n;
				if (j > n / 2)
					j -= n;
			} else if (j < 0) {
				j = -(-j % n);
				if (j < -n / 2)
					j += n;
			}
			if (j == 0)
				return;
			e2 = stack;		/* check the stack */
			i = n;
			for (;;) {
				if (!e2) {
					fprintf(stderr, "svg roll: Stack empty\n");
					ps_error = 1;
					return;
				}
				if (--i <= 0)
					break;
				e2 = e2->next;
			}
			if (j > 0) {
				while (j-- > 0) {
					e = stack;
					stack = e->next;
					e->next = e2->next;
					e2->next = e;
					e2 = e;
				}
				return;
			}
			while (j++ < 0) {
				e = stack;
				for (i = 0; i < n - 2; i++)
					e = e->next;
				e2 = e->next;
				e->next = e2->next;
				e2->next = stack;
				stack = e2;
			}
			return;
		}
		if (strcmp(op, "repbra") == 0) {
			int i;

			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			w = pop_free_val();
			i = pop_free_val();
			h = pop_free_val();
			s = pop_free_str();
			if (!s) {
				fprintf(stderr, "svg repbra: No string\n");
				ps_error = 1;
				return;
			}
			fprintf(fout,
				"<text x=\"%.2f\" y=\"%.2f\">",
				x + 4, y - h);
			xml_str_out(s + 1);
			fprintf(fout,
				"</text>\n"
				"<path stroke=\"currentColor\" fill=\"none\"\n"
				"	d=\"M%.2f %.2f",
				x, y);
			if (i != 1)
				fprintf(fout, "m0 20v-20");
			fprintf(fout, "h%.2f", w);
			if (i != 0)
				fprintf(fout, "v20");
			fprintf(fout, "\"/>\n");
			free(s);
			return;
		}
		if (strcmp(op, "repeat") == 0) {
			e = pop(SEQ);		/* sequence */
			if (!e)
				return;
			n = pop_free_val();	/* n times */
			if ((unsigned) n >= 100) {
				fprintf(stderr, "svg repeat: Too high value\n");
				ps_error = 1;
			}
			while (--n >= 0) {
				if (seq_exec(e))
					break;		/* exit */
				if (ps_error)
					break;
			}
			elt_free(e);
			return;
		}
		if (strcmp(op, "rotate") == 0) {
			setg(0);
#if 1
			h = 360 - pop_free_val();
			gcur.rotate += h;
#else
			h = pop_free_val();
			gcur.rotate -= h;
#endif
			h = h * M_PI / 180;
			x = cx;
			cx = x * cos(h) + cy * sin(h);
			cy = -x * sin(h) + cy * cos(h);
			return;
		}
		break;
	case 'S':
		if (strcmp(op, "SL") == 0) {
			float c1, c2, c3, c4, c5, c6, l2;
			float a1, a2, a3, a4, a5, a6, m1, m2;

			setg(1);
			m2 = yoffs - pop_free_val();
			m1 = xoffs + pop_free_val();
			a6 = pop_free_val();
			a5 = pop_free_val();
			a4 = pop_free_val();
			a3 = pop_free_val();
			a2 = pop_free_val();
			a1 = pop_free_val();
			l2 = pop_free_val();
			pop_free_val();		// always '0'
			c6 = pop_free_val();
			c5 = pop_free_val();
			c4 = pop_free_val();
			c3 = pop_free_val();
			c2 = pop_free_val();
			c1 = pop_free_val();
			fprintf(fout,
				"<path fill=\"currentColor\"\n"
				"	d=\"M%.2f %.2fc%.2f %.2f %.2f %.2f %.2f %.2f\n"
				"	v%.2fc%.2f %.2f %.2f %.2f %.2f %.2f\"/>\n",
				m1, m2, a1, -a2, a3, -a4, a5, -a6,
				-l2, c1, -c2, c3, -c4, c5, -c6);
			return;
		}
		if (strcmp(op, "SLW") == 0) {
			gcur.linewidth = pop_free_val();
			return;
		}
		break;
	case 's':
		if (strcmp(op, "scale") == 0) {
			y = pop_free_val();
			x = pop_free_val();
			xoffs /= x;
			yoffs /= y;
			cx /= x;
			cy /= y;
			gcur.xscale *= x;
			gcur.yscale *= y;
			return;
		}
		if (strcmp(op, "scalefont") == 0) {
			w = pop_free_val();
			gcur.font_s = w;
			return;
		}
		if (strcmp(op, "search") == 0) {
			char *p;

			e = pop(STR);			/* seek */
			e2 = pop(STR);			/* string */
			if (!e || !e2
			 || e->u.s[0] != '(' || e2->u.s[0] != '(') {
				fprintf(stderr, "svg search: No string\n");
				ps_error = 1;
				return;
			}
			p = strstr(&e2->u.s[1], &e->u.s[1]);
			if (p) {
				struct elt_s *e3;
				int l1, l2, l3;

				l1 = p - e2->u.s;
				l2 = strlen(e->u.s);
				l3 = strlen(e2->u.s) - l2 - l1 + 2;
				e3 = elt_new();
				if (!e3)
					return;
				e3->type = STR;
				e3->u.s = malloc(l3);
				e3->u.s[0] = '(';
				memcpy(&e3->u.s[1],
					&e2->u.s[l1 + l2 - 2],
					l3 - 1);
				e3->u.s[l1 + l2 - 1] = '\0';
				push(e3);
				push(e);
				e2->u.s[l1] = '\0';
				push (e2);
				e = elt_new();
				if (!e)
					return;
				e->type = VAL;
				e->u.v = 1;
			} else {
				push(e2);
				free(e->u.s);
				e->type = VAL;
				e->u.v = 0;
			}
			push(e);
			return;
		}
		if (strcmp(op, "selectfont") == 0) {
			w = pop_free_val();
			s = pop_free_str();
			if (!s
			 || *s != '/'
			 || strlen(s) >= sizeof gcur.font_n) {
				fprintf(stderr, "svg selectfont: No / bad font\n");
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
			strcpy(gcur.font_n, s + 1);
			gcur.font_s = w;
			free(s);
			return;
		}
		if (strcmp(op, "sep0") == 0) {
			x = pop_free_val();
			w = pop_free_val();
			fprintf(fout,
				"<path stroke=\"currentColor\" fill=\"none\"\n"
				"	d=\"M%.2f %.2fh%.2f\"/>\n",
					xoffs + x, yoffs, w);
			return;
		}
		if (strcmp(op, "setdash") == 0) {
			char *p;

			n = pop_free_val();
			e = pop(BRK);
			if (!e) {
				fprintf(stderr, "svg setdash: Bad pattern\n");
				ps_error = 1;
				return;
			}
			e = e->u.e;
			if (!e) {
				gcur.dash[0] = '\0';
				return;
			}
			p = gcur.dash;
			if (n != 0)
				p += sprintf(p, " stroke-dashoffset=\"%d\"", n);
			p += sprintf(p, " stroke-dasharray=\"");
			do {
				if (e->type != VAL) {
					fprintf(stderr, "svg setdash: Bad pattern type\n");
					ps_error = 1;
					return;
				}
				if (p >= &gcur.dash[sizeof gcur.dash] - 10) {
					fprintf(stderr, "svg setdash: Pattern too wide\n");
					ps_error = 1;
					return;
				}
				p += sprintf(p, "%d,", (int) e->u.v);
				e = e->next;
			} while (e);
			p--;
			sprintf(p, "\"");
			return;
		}
		if (strcmp(op, "setfont") == 0) {
			return;
		}
		if (strcmp(op, "setgray") == 0) {
			n = pop_free_val() * 255;
			gcur.rgb = (n << 16) | (n << 8) | n;
			return;
		}
		if (strcmp(op, "setlinewidth") == 0) {
			gcur.linewidth = pop_free_val();
			return;
		}
//fixme: use 'use' for flags
		if (strcmp(op, "sfu") == 0) {
			setg(1);
			h = pop_free_val();
			n = pop_free_val();
			sym = ps_sym_lookup("x");
			x = xoffs + sym->e->u.v + 3.5;
			sym = ps_sym_lookup("y");
			y = yoffs - sym->e->u.v;
			fprintf(fout,
				"<path d=\"M%.2f %.2fv%.2f\" stroke=\"currentColor\" fill=\"none\"/>\n"
				"<path fill=\"currentColor\"\n"
				"	d=\"",
				x, y, -h);
			y -= h;
			if (n == 1) {
				fprintf(fout,
					"M%.2f %.2fc0.6 5.6 9.6 9 5.6 18.4\n"
					"	1.6 -6 -1.3 -11.6 -5.6 -12.8\n",
					x, y);
			} else {
				while (--n >= 0) {
					fprintf(fout,
						"M%.2f %.2fc0.9 3.7 9.1 6.4 6 12.4\n"
						"	1 -5.4 -4.2 -8.4 -6 -8.4\n",
						x, y);
					y += 5.4;
				}
			}
			fprintf(fout, "\"/>\n");
			return;
		}
		if (strcmp(op, "sfd") == 0) {
			setg(1);
			h = pop_free_val();
			n = pop_free_val();
			sym = ps_sym_lookup("x");
			x = xoffs + sym->e->u.v - 3.5;
			sym = ps_sym_lookup("y");
			y = yoffs - sym->e->u.v;
			fprintf(fout,
				"<path d=\"M%.2f %.2fv%.2f\" stroke=\"currentColor\" fill=\"none\"/>\n"
				"<path fill=\"currentColor\"\n"
				"	d=\"",
				x, y, -h);
			y -= h;
			if (n == 1) {
				fprintf(fout,
					"M%.2f %.2fc0.6 -5.6 9.6 -9 5.6 -18.4\n"
					"	1.6 6 -1.3 11.6 -5.6 12.8\n",
					x, y);
			} else {
				while (--n >= 0) {
					fprintf(fout,
						"M%.2f %.2fc0.9 -3.7 9.1 -6.4 6 -12.4\n"
						"	1 5.4 -4.2 8.4 -6 8.4\n",
						x, y);
					y -= 5.4;
				}
			}
			fprintf(fout, "\"/>\n");
			return;
		}
		if (strcmp(op, "sfs") == 0) {
			setg(1);
			h = pop_free_val();
			n = pop_free_val();
			sym = ps_sym_lookup("x");
			x = xoffs + sym->e->u.v;
			sym = ps_sym_lookup("y");
			y = yoffs - sym->e->u.v - 1;
			if (h > 0) {
				x += 3.5;
				y -= 1;
				fprintf(fout,
					"<path d=\"M%.2f %.2fv%.2f\" stroke=\"currentColor\" fill=\"none\"/>\n"
					"<path fill=\"currentColor\"\n"
					"	d=\"",
					x, y, -h + 1);
				y -= h - 1;
				while (--n >= 0) {
					fprintf(fout,
						"M%.2f %.2fl7 3.2 0 3.2 -7 -3.2z\n",
						x, y);
					y += 5.4;
				}
			} else {
				x -= 3.5;
				y += 1;
				fprintf(fout,
					"<path d=\"M%.2f %.2fv%.2f\" stroke=\"currentColor\" fill=\"none\"/>\n"
					"<path fill=\"currentColor\"\n"
					"	d=\"",
					x, y, -h - 1);
				y -= h + 1;
				while (--n >= 0) {
					fprintf(fout,
						"M%.2f %.2fl7 -3.2 0 -3.2 -7 3.2z\n",
						x, y);
					y -= 5.4;
				}
			}
			fprintf(fout, "\"/>\n");
			return;
		}
		if (strcmp(op, "sgu") == 0) {
			setg(1);
			h = pop_free_val();
			n = pop_free_val();
			sym = ps_sym_lookup("x");
			x = xoffs + sym->e->u.v + 1.6;
			sym = ps_sym_lookup("y");
			y = yoffs - sym->e->u.v;
			fprintf(fout,
				"<path d=\"M%.2f %.2fv%.2f\" stroke=\"currentColor\" fill=\"none\"/>\n"
				"<path fill=\"currentColor\"\n"
				"	d=\"",
				x, y, -h);
			y -= h;
			if (n == 1) {
				fprintf(fout,
					"M%.2f %.2fc0.6 3.4 5.6 3.8 3 10\n"
					"	1.2 -4.4 -1.4 -7 -3 -7\n",
					x, y);
			} else {
				while (--n >= 0) {
					fprintf(fout,
						"M%.2f %.2fc1 3.2 5.6 2.8 3.2 8\n"
						"	1.4 -4.8 -2.4 -5.4 -3.2 -5.2\n",
					x, y);
					y += 3.5;
				}
			}
			fprintf(fout, "\"/>\n");
			return;
		}
		if (strcmp(op, "sgd") == 0) {
			setg(1);
			h = pop_free_val();
			n = pop_free_val();
			sym = ps_sym_lookup("x");
			x = xoffs + sym->e->u.v - 1.6;
			sym = ps_sym_lookup("y");
			y = yoffs - sym->e->u.v;
			fprintf(fout,
				"<path d=\"M%.2f %.2fv%.2f\" stroke=\"currentColor\" fill=\"none\"/>\n"
				"<path fill=\"currentColor\"\n"
				"	d=\"",
				x, y, -h);
			y -= h;
			if (n == 1) {
				fprintf(fout,
					"M%.2f %.2fc0.6 -3.4 5.6 -3.8 3 -10\n"
					"	1.2 4.4 -1.4 7 -3 7\n",
					x, y);
			} else {
				while (--n >= 0) {
					fprintf(fout,
						"M%.2f %.2fc1 -3.2 5.6 -2.8 3.2 -8\n"
						"	1.4 4.8 -2.4 5.4 -3.2 5.2\n",
						x, y);
					y -= 3.5;
				}
			}
			fprintf(fout, "\"/>\n");
			return;
		}
		if (strcmp(op, "sgs") == 0) {
			setg(1);
			h = pop_free_val();
			n = pop_free_val();
			sym = ps_sym_lookup("x");
			x = xoffs + sym->e->u.v + 1.6;
			sym = ps_sym_lookup("y");
			y = yoffs - sym->e->u.v;
			fprintf(fout,
				"<path d=\"M%.2f %.2fv%.2f\" stroke=\"currentColor\" fill=\"none\"/>\n"
				"<path fill=\"currentColor\"\n"
				"	d=\"",
				x, y, -h);
			y -= h;
			while (--n >= 0) {
				fprintf(fout,
					"M%.2f %.2fl3 1.5 0 2 -3 -1.5z\n",
					x, y);
				y += 3;
			}
			fprintf(fout, "\"/>\n");
			return;
		}
		if (strcmp(op, "sfz") == 0) {
			xysym(op, D_sfz);
			s = pop_free_str();
			if (s)
				free(s);
			return;
		}
		if (strcmp(op, "sgno") == 0) {
			xysym(op, D_sgno);
			return;
		}
		if (strcmp(op, "show") == 0) {
			show('s');
			return;
		}
		if (strcmp(op, "showb") == 0) {
			show('b');
			return;
		}
		if (strcmp(op, "showc") == 0) {
			show('c');
			return;
		}
		if (strcmp(op, "showr") == 0) {
			show('r');
			return;
		}
		if (strcmp(op, "showerror") == 0) {
			def_use(D_showerror);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			fprintf(fout, "<use x=\"%.2f\" y=\"%.2f\" xlink:href=\"#%s\"/>\n",
				x, y, op);
			return;
		}
		if (strcmp(op, "sld") == 0) {
			xysym(op, D_sld);
			return;
		}
		if (strcmp(op, "snap") == 0) {
			xysym(op, D_snap);
			return;
		}
		if (strcmp(op, "sphr") == 0) {
			xysym(op, D_sphr);
			return;
		}
		if (strcmp(op, "spclef") == 0) {
			def_use(D_pclef);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			fprintf(fout, "<use x=\"%.2f\" y=\"%.2f\" xlink:href=\"#pclef\"/>\n",
				x, y);
			return;
		}
		if (strcmp(op, "setrgbcolor") == 0) {
			int r, g, b;

			b = pop_free_val() * 255;
			g = pop_free_val() * 255;
			r = pop_free_val() * 255;
			gcur.rgb = (r << 16) | (g << 8) | b;
			return;
		}
		if (strcmp(op, "staff") == 0) {
			gcur.linewidth = DLW;
			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			n = pop_free_val();
			w = pop_free_val();
			fprintf(fout,
				"<path stroke=\"currentColor\" fill=\"none\"\n"
				"	d=\"M%.2f %.2f", x, y);
			for (;;) {
				fprintf(fout, "h%.2f", w);
				if (--n <= 0)
					break;
				fprintf(fout, "m-%.2f -6", w);
			}
			fprintf(fout, "\"/>\n");
			return;
		}
		if (strcmp(op, "stc") == 0) {
			xysym(op, D_stc);
			return;
		}
		if (strcmp(op, "stroke") == 0) {
			if (!path) {
				fprintf(stderr, "svg: 'stroke' with no path\n");
//				ps_error = 1;
				return;
			}
			path_end();
			fprintf(fout, "\t\" stroke=\"currentColor\" fill=\"none\"%s/>\n",
					gcur.dash);
			return;
		}
		if (strcmp(op, "su") == 0
		 || strcmp(op, "sd") == 0) {
			stem(op);
			return;
		}
		if (strcmp(op, "stsig") == 0) {
			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			s = pop_free_str();
			if (!s) {
				fprintf(stderr, "svg: No string\n");
				ps_error = 1;
				return;
			}
			fprintf(fout, "<g font-family=\"serif\" font-size=\"18\" font-weight=\"bold\" font-style=\"normal\"\n"
				"	transform=\"translate(%.2f,%.2f) scale(1.2,1)\">\n"
				"	<text y=\"-7\" text-anchor=\"middle\">%s</text>\n"
				"</g>\n",
				x, y, s + 1);
			free(s);
			return;
		}
		if (strcmp(op, "sub") == 0) {
			x = pop_free_val();
			if (!stack || stack->type != VAL) {
				fprintf(stderr, "svg: Bad value for sub\n");
				ps_error = 1;
				return;
			}
			stack->u.v -= x;
			return;
		}
		if (strcmp(op, "sbclef") == 0) {
			xysym(op, D_sbclef);
			return;
		}
		if (strcmp(op, "scclef") == 0) {
			xysym(op, D_scclef);
			return;
		}
		if (strcmp(op, "sh0") == 0) {
			xysym(op, D_sh0);
			return;
		}
		if (strcmp(op, "sh1") == 0) {
			xysym(op, D_sh1);
			return;
		}
		if (strcmp(op, "sh4") == 0) {
			n = pop_free_val();
			switch (n) {
			case 1:
				xysym("sh1", D_sh1);
				break;
			case 2:
				xysym("sh0", D_sh0);
				break;
			case 3:
				xysym("sh513", D_sh513);
				break;
			default:
				xysym("dsh0", D_dsh0);
				break;
			}
			return;
		}
		if (strcmp(op, "sh513") == 0) {
			xysym(op, D_sh513);
			return;
		}
		if (strcmp(op, "srep") == 0) {
			xysym(op, D_srep);
			return;
		}
		if (strcmp(op, "stclef") == 0) {
			xysym(op, D_stclef);
			return;
		}
		if (strcmp(op, "stringwidth") == 0) {
			s = pop_free_str();
			if (!s || *s != '(') {
				fprintf(stderr, "svg stringwidth: No string\n");
				ps_error = 1;
				return;
			}
			e = elt_new();
			if (!e)
				return;
			e->type = VAL;
			e->u.v = strw(s + 1);
			push(e);
			e = elt_new();
			if (!e)
				return;
			e->type = VAL;
			e->u.v = gcur.font_s;
			push(e);
			return;
		}
		if (strcmp(op, "svg") == 0) {
			e = elt_new();
			if (!e)
				return;
			e->type = VAL;
			e->u.v = 1;
			push(e);
			return;
		}
		break;
	case 'T':
		if (strcmp(op, "T") == 0) {
translate:
//fixme:test
//			setg(1);
			y = pop_free_val();
			x = pop_free_val();
			xoffs += x;
			yoffs -= y;
			cx -= x;
			cy -= y;
//fprintf(stderr, "T %.2f %.2f -> %.2f %.2f\n", x, y, xoffs, yoffs);
			return;
		}
		break;
	case 't':
		if (strcmp(op, "tclef") == 0) {
			xysym(op, D_tclef);
			return;
		}
		if (strcmp(op, "thbar") == 0) {
			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val() + 1.5;
			h = pop_free_val();
			fprintf(fout,
				"<path stroke=\"currentColor\" fill=\"none\" stroke-width=\"3\"\n"
				"	d=\"M%.2f %.2fv%.2f\"/>\n",
				x, y, -h);
			return;
		}
		if (strcmp(op, "thumb") == 0) {
			xysym(op, D_thumb);
			return;
		}
		if (strcmp(op, "translate") == 0)
			goto translate;
		if (strcmp(op, "trem") == 0) {
			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val() - 4.5;
			n = pop_free_val();
			fprintf(fout, "<path fill=\"currentColor\" d=\"m%.2f %.2f\n\t",
				x, y);
			for (;;) {
				fputs("l9 -3v3l-9 3z", fout);
				if (--n <= 0)
					break;
				fputs("m0 5.4", fout);
			}
			fputs("\"/>", fout);
			return;
		}
		if (strcmp(op, "trl") == 0) {
			xysym(op, D_trl);
			return;
		}
		if (strcmp(op, "true") == 0) {
			e = elt_new();
			if (!e)
				return;
			e->type = VAL;
			e->u.v = 1;
			push(e);
			return;
		}
		if (strcmp(op, "tsig") == 0) {
			char *d;

			setg(1);
			y = yoffs - pop_free_val() - 0.5;
			x = xoffs + pop_free_val();
			d = pop_free_str();
			s = pop_free_str();
			if (!d || !s) {
				fprintf(stderr, "svg: No string\n");
				if (d)
					free(d);
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
			fprintf(fout, "<g font-family=\"serif\" font-size=\"16\" font-weight=\"bold\" font-style=\"normal\"\n"
				"	transform=\"translate(%.2f,%.2f) scale(1.2,1)\">\n"
				"	<text text-anchor=\"middle\">%s</text>\n"
				"	<text y=\"-12\" text-anchor=\"middle\">%s</text>\n"
				"</g>\n",
				x, y, d + 1, s + 1);
			free(d);
			free(s);
			return;
		}
		if (strcmp(op, "tubr") == 0
		 || strcmp(op, "tubrl") == 0) {
			float dx, dy;
			int h;

			setg(1);
			y = yoffs - pop_free_val();
			x = xoffs + pop_free_val();
			dy = pop_free_val();
			dx = pop_free_val();
			if (op[4] == 'l') {
				h = 3;
				y -= 3;
			} else {
				h = -3;
				y += 3;
			}
			fprintf(fout,
				"<path stroke=\"currentColor\" fill=\"none\"\n"
				"	d=\"M%.2f %.2fv%dl%.2f %.2fv%d\"/>\n",
				x, y, h, dx, -dy, -h);
			return;
		}
		if (strcmp(op, "turn") == 0) {
			xysym(op, D_turn);
			return;
		}
		if (strcmp(op, "turnx") == 0) {
			xysym(op, D_turnx);
			return;
		}
		break;
	case 'u':
		if (strcmp(op, "upb") == 0) {
			xysym(op, D_upb);
			return;
		}
		if (strcmp(op, "umrd") == 0) {
			xysym(op, D_umrd);
			return;
		}
		break;
	case 'w':
		if (strcmp(op, "wedge") == 0) {
			xysym(op, D_wedge);
			return;
		}
		if (strcmp(op, "wln") == 0) {
			setg(1);
			y = pop_free_val();
			x = pop_free_val();
			w = pop_free_val();
			fprintf(fout, "<path stroke=\"currentColor\" fill=\"none\" stroke-width=\"0.8\"\n"
				"	d=\"M%.2f %.2fh%.2f\"/>\n",
				xoffs + x, yoffs - y, w);
			return;
		}
		if (strcmp(op, "where") == 0) {
			s = pop_free_str();		/* symbol */
			if (!s || *s != '/') {
				fprintf(stderr, "svg where: No / bad symbol\n");
				if (s)
					free(s);
				ps_error = 1;
				return;
			}
			e = elt_new();
			if (!e)
				return;
			e->type = VAL;
			sym = ps_sym_lookup(&s[1]);
			if (!sym) {
				e->u.v = 0;
			} else {
				e->u.v = 1;
				e2 = elt_new();		/* dictionnary */
				if (!e2)
					return;
				e2->type = VAL;
				e2->u.v = 0;
				push(e2);
			}
			free(s);
			push(e);
			return;
		}
		break;
	case 'x':
		if (strcmp(op, "xymove") == 0) {
			cy = y = pop_free_val();
			cx = x = pop_free_val();
			setxory("x", x);
			setxory("y", y);
			return;
		}
		break;
	}
	fprintf(stderr, "svg: Symbol '%s' not defined\n", op);
	ps_error = 1;
}

void svg_write(char *buf, int len)
{
	int l;
	struct elt_s *e, *e2;
	unsigned char c, *p, *q, *r;

	if (ps_error)
		return;

	p = (unsigned char *) buf;
#if 0
	if (strncmp((char *) p, "%svg ", 5) == 0) {	/* %%beginsvg */
		fwrite(p + 5, 1, len - 5, fout);
		fputs("\n", fout);
		return;
	}
#endif

	/* scan the string */
	while (--len >= 0) {
		c = *p++;
		switch (c) {
		case ' ':
		case '\t':
		case '\n':
			continue;
		case '{':
		case '[':		/* treat '[' as '{' */
			e = elt_new();
			if (!e)
				return;
			in_cnt++;
			e->type = STR;
			e->u.s = strdup(c == '{' ? "{" : "[");
			push(e);
			break;
		case '}':
		case ']':
			in_cnt--;
			if (in_cnt < 0) {
				if (c == '}')
					fprintf(stderr, "svg: '}' without '{'\n");
				else
					fprintf(stderr, "svg: ']' without '['\n");
				ps_error = 1;
				return;
			}
			e = elt_new();
			if (!e)
				return;

			/* create a container with elements in direct order */
			e->u.e = NULL;
			if (c == '}') {
				e->type = SEQ;
				c = '{';
			} else {
				e->type = BRK;
				c = '[';
			}
			for (;;) {
				e2 = stack;
				stack = stack->next;
				if (e2->type == STR
				 && (e2->u.s[0] == '['
				  || e2->u.s[0] == '{'))
					break;
				e2->next = e->u.e;
				e->u.e = e2;
			}
			if (e2->u.s[0] != c) {
				fprintf(stderr, "svg: '%c' found before '%c'\n",
					e2->u.s[0], c);
				ps_error = 1;
				return;
			}
			elt_free(e2);
			push(e);
			break;
		case '%':
			q = p;
			while (--len >= 0) {
				c = *p++;
				if (c == '\n')
					break;
			}
			if ((char *) q != &buf[1] && q[-2] != '\n')
				break;
			if (strncmp((char *) q, "A ", 2) == 0) {	/* annotation */
				char type;
				int row , col, h;
				float x, y, w;

				q += 2;
				type = *q++;
				if (type != 'b' && type != 'e') {	/* if not beam */
					sscanf((char *) q + 1, "%d %d %f %f %f %d",
						&row, &col, &x, &y, &w, &h);
				} else {
					sscanf((char *) q + 1, "%d %d %f %f",
						&row, &col, &x, &y);
					w = h = 6;
				}
				fprintf(fout, "<abc type=\"%c\" row=\"%d\" col=\"%d\" x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%d\"/>\n",
					type, row, col, xoffs + x, yoffs - y - h, w, h);
				break;
			}
			if (strncmp((char *) q, " --- title", 10) == 0) { /* title info */
				setg(1);
				if (q[10] == 's') {		/* subtitle */
					q += 14;
					fprintf(fout, "<!-- subtitle: %.*s -->\n",
							(int) (p - q - 1), q);
					break;
				}
				q += 11;
				fprintf(fout, "<!-- title: %.*s -->\n",
						(int) (p - q -1), q);
				break;
			}
			break;
		case '(':
			q = p - 1;
			l = 1;
			for (;;) {
				switch (*p++) {
				case '\\':
					p++;
					l--;
					continue;
				default:
					continue;
				case ')':
					break;
				}
				break;
			}
			len -= p - q - 1;
			l += p - q - 1;
			p = q;
			e = elt_new();
			if (!e)
				return;
			e->type = STR;
			r = malloc(l);
			e->u.s = (char *) r;
			for (;;) {
				c = *p++;
				switch (c) {
				case '\\':
					*r++ = *p++;
					continue;
				default:
					*r++ = c;
					continue;
				case ')':
					break;
				}
				break;
			}
			*r = '\0';
			push(e);
			break;
		default:
			q = p - 1;
			while (--len >= 0) {
				c = *p++;
				switch (c) {
				case '(':
				case ' ':
				case '\t':
				case '\n':
				case '{':
				case '}':
				case '[':
				case ']':
				case '%':
				case '/':
					break;
				default:
					continue;
				}
				break;
			}
			if (len >= 0) {
				p--;
				len++;
			}
			if (isdigit((unsigned) *q) || *q == '-' || *q == '.') {
				int i;
				float v;

				e = elt_new();
				if (!e)
					return;
				e->type = VAL;
				c = *p;
				*p = '\0';
				if (q[1] == '#') {
					i = strtol((char *) q + 2, 0, 8);
					e->u.v = i;
				} else if (q[2] == '#') {
					i = strtol((char *) q + 3, 0, 16);
					e->u.v = i;
				} else {
					if (sscanf((char *) q, "%f", &v) != 1) {
						fprintf(stderr, "svg: Bad numeric value in '%s'",
							buf);
						v = 0;
					}
					e->u.v = v;
				}
				*p = c;
			} else {
				if (!in_cnt) {
					if (*q != '/') {	/* operator */
						c = *p;
						*p = '\0';
						ps_exec((char *) q);
						if (ps_error)
							return;
						*p = c;
						break;
					}
				} else if (strncmp((char *) q, "pdfmark", 7) == 0) {
					in_cnt--;
					for (;;) {
						e = pop(stack->type);
						if (e->type == STR
						 && (e->u.s[0] == '['
						  || e->u.s[0] == '{'))
							break;
						elt_free(e);
					}
					elt_free(e);
					break;
				}
				l = p - q;
				r = malloc(l + 1);
				memcpy(r, q, l);
				r[l] = '\0';
				e = elt_new();
				if (!e)
					return;
				e->type = STR;
				e->u.s = (char *) r;
			}
			push(e);
			break;
		}
	}
}

int svg_output(FILE *out, const char *fmt, ...)
{
	va_list args;
	char tmp[128];

	va_start(args, fmt);
	vsnprintf(tmp, sizeof tmp, fmt, args);
	va_end(args);
	svg_write(tmp, strlen(tmp));
	return 0;
}

void svg_close(void)
{
	struct elt_s *e, *e2;

	setg(0);
	fputs("</svg>\n", fout);
	e = stack;
	if (e) {
		stack = NULL;
		fprintf(stderr, "svg close: stack not empty ");
		elt_lst_dump(e);
		fprintf(stderr, "\n");
		do {
			e2 = e->next;
			elt_free(e);
			e = e2;
		} while (e);
	}
}
abcm2ps-8.5.2/syms.c0000644000175000017500000006233312451551247012323 0ustar  jefjef/*
 * Postscript definitions.
 *
 * This file is part of abcm2ps.
 *
 * Copyright (C) 1998-2015 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.
 */

#include <string.h>

#include "abc2ps.h"

static char ps_head[] =
	"/xymove{/x 2 index def/y 1 index def M}!\n"

	/* str showc - center at current pt */
	"/showc{dup stringwidth pop .5 mul neg 0 RM show}!\n"

	/* str showr - show right-aligned */
	"/showr{dup stringwidth pop neg 0 RM show}!\n"

	/* str showb - show in box */
	"/showb{	dup currentpoint 3 -1 roll show\n"
	"	.6 SLW\n"
	"	exch 2 sub exch 3 sub 3 -1 roll\n"
	"	stringwidth pop 4 add\n"
	"	currentfont/ScaleMatrix get 0 get .8 mul\n"
	"	4 add rectstroke}!\n"

	/* x y tclef - treble clef */
	"/utclef{<95200072\n"
	"	0000ff2e01c2030c\n"
	"	00ac0056\n"
	"	0064007f006400f400e00112\n"
	"	0176011c01bc0056013a0012\n"
	"	00c8ffde002700120015009a\n"
	"	0006014f0072017f00f101e8\n"
	"	0149023f0140026d012f02ba\n"
	"	00fc029900d1025100d60200\n"
	"	00e700f500fa008a0107ffc2\n"
	"	010dff6200f4ff3c00baff3b\n"
	"	006aff3a003cff98007dffc0\n"
	"	00d2ffe90102ff5b009cff57\n"
	"	00b3ff4600f8ff3200f6ffb3\n"
	"	00ec009200cf010900c4021c\n"
	"	00c4027600c402be01240304\n"
	"	015c02bc0163021e013a01e3\n"
	"	00f001790039013b003b00a7\n"
	"	0044000e00cfffee01370022\n"
	"	018d0063015400e200e700d2\n"
	"	00a000c6007e008f00ac0056\n"
	"	><0b000132050a>}cvlit def\n"
	"/tclef{gsave T -10 0 T .045 dup scale utclef ufill grestore}!\n"
	"/stclef{gsave T -10 0 T .037 dup scale utclef ufill grestore}!\n"

	/* x y octu - upper '8' */
	"/octu{/Times-Roman 12 selectfont M -2.5 0 RM(8)show}!\n"
	/* x y octl - lower '8' */
	"/octl{/Times-Roman 12 selectfont M -3.5 0 RM(8)show}!\n"

	/* x y bclef - bass clef */
	"/ubclef{<95200046\n"
	"	00000050019a0244\n"
	"	00010057\n"
	"	007d007a00df00a500ff0143\n"
	"	012a022700580239003f01aa\n"
	"	007a01fa00dc0194009b015c\n"
	"	005d012d00280172003101b4\n"
	"	00460241013f023c01430180\n"
	"	014200d100d9007800010057\n"
	"	01660151\n"
	"	016601750199017301990151\n"
	"	0199012c0166012d01660151\n"
	"	016401d2\n"
	"	016401f6019701f4019701d2\n"
	"	019701ac016401ad016401d2\n"
	"	><0b000126050a0122050a0122050a>}cvlit def\n"
	"/bclef{gsave T -10 0 T .045 dup scale ubclef ufill grestore}!\n"
	"/sbclef{gsave T -10 3 T .037 dup scale ubclef ufill grestore}!\n"

	/* x y cclef */
	"/ucclef{<95200066\n"
	"	006effbe01e70256\n"
	"	00d10108\n"
	"	00d10002\n"
	"	00c40002\n"
	"	00c40213\n"
	"	00d10213\n"
	"	00d10113\n"
	"	00ea012700fa013701100180\n"
	"	011e0161011d014d0148013a\n"
	"	01a2011801a80244011f01f3\n"
	"	015301e0013a01a3011401a6\n"
	"	00ba01cc01350256019f01eb\n"
	"	01e7019c01a000fa01190131\n"
	"	0109010a\n"
	"	011900e4\n"
	"	01a0011b01e70079019f002a\n"
	"	0135ffbe00ba00490114006f\n"
	"	013a007201530035011f0022\n"
	"	01a8ffd101a200fd014800db\n"
	"	011d00c8011b00bd0110009b\n"
	"	00fa00e400ea00f400d10108\n"
	"	006e0213\n"
	"	00a70213\n"
	"	00a70002\n"
	"	006e0002\n"
	"	006e0213\n"
	"	><0b000125032605220326050a0124030a>}cvlit def\n"
	"/cclef{gsave T -12 0 T .045 dup scale ucclef ufill grestore}!\n"
	"/scclef{gsave T -12 2 T .037 dup scale ucclef ufill grestore}!\n"

	/* x y pclef */
	"/pclef{	exch 2.7 sub exch 2 add 5.4 20 1.4 SLW rectstroke}!\n"
	"/spclef{pclef}!\n"

	/* t dx dy x y bm - beam, depth t */
	"/bm{	M 3 copy RL neg 0 exch RL neg exch neg exch RL 0 exch RL fill}!\n"

	/* str x y bnum - tuplet number / ratio */
	"/bnum{M/Times-Italic 12 selectfont showc}!\n"
	/* same with clearing below the number */
	"/bnumb{	currentgray/Times-Italic 12 selectfont\n"
	"	3 index stringwidth pop 4 add\n"
	"	dup .5 mul neg 4 index add 3 index 3 -1 roll 8\n"
	"	1.0 setgray rectfill setgray M showc}!\n"

	/* dx dy x y tubr - tuplet bracket */
	"/tubr{3 sub M 0 3 RL RL 0 -3 RL dlw stroke}!\n"
	"/tubrl{3 add M 0 -3 RL RL 0 3 RL dlw stroke}!\n"

	/* x y r00 - longa rest */
	"/r00{	xymove -1.5 -6 RM currentpoint 3 12 rectfill}!\n"

	/* x y r0 - breve rest */
	"/r0{	xymove -1.5 0 RM currentpoint 3 6 rectfill}!\n"

	/* x y r1 - rest */
	"/r1{	xymove -3.5 3 RM currentpoint 7 3 rectfill}!\n"

	/* x y r2 - half rest */
	"/r2{	xymove -3.5 0 RM currentpoint 7 3 rectfill}!\n"

	/* x y r4 - quarter rest */
	"/r4{	xymove\n"
	"	-1 8.5 RM\n"
	"	3.6 -5.1 RL\n"
	"	-2.1 -5.2 RL\n"
	"	2.2 -4.3 RL\n"
	"	-2.6 2.3 -5.1 0 -2.4 -2.6 RC\n"
	"	-4.8 3 -1.5 6.9 1.4 4.1 RC\n"
	"	-3.1 4.5 RL\n"
	"	1.9 5.1 RL\n"
	"	-1.5 3.5 RL\n"
	"	fill}!\n"

	/* 1/8 .. 1/64 rest element */
	"/r8e{	-1.5 -1.5 -2.4 -2 -3.6 -2 RC\n"
	"	2.4 2.8 -2.8 4 -2.8 1.2 RC\n"
	"	0 -2.7 4.3 -2.4 5.9 -0.6 RC\n"
	"	fill}!\n"

	/* x y r8 - eighth rest */
	"/r8{	xymove\n"
	"	.5 SLW 3.3 4 RM\n"
	"	-3.4 -9.6 RL stroke\n"
	"	x y M 3.4 4 RM r8e}!\n"

	/* x y r16 - 16th rest */
	"/r16{	xymove\n"
	"	.5 SLW 3.3 4 RM\n"
	"	-4 -15.6 RL stroke\n"
	"	x y M 3.4 4 RM r8e\n"
	"	x y M 1.9 -2 RM r8e}!\n"

	/* x y r32 - 32th rest */
	"/r32{	xymove\n"
	"	.5 SLW 4.8 10 RM\n"
	"	-5.5 -21.6 RL stroke\n"
	"	x y M 4.9 10 RM r8e\n"
	"	x y M 3.4 4 RM r8e\n"
	"	x y M 1.9 -2 RM r8e}!\n"

	/* x y r64 - 64th rest */
	"/r64{	xymove\n"
	"	.5 SLW 4.8 10 RM\n"
	"	-7 -27.6 RL stroke\n"
	"	x y M 4.9 10 RM r8e\n"
	"	x y M 3.4 4 RM r8e\n"
	"	x y M 1.9 -2 RM r8e\n"
	"	x y M 0.4 -8 RM r8e}!\n"

	/* x y r128 - 128th rest */
	"/r128{	xymove\n"
	"	.5 SLW 5.8 16 RM\n"
	"	-8.5 -33.6 RL stroke\n"
	"	x y M 5.9 16 RM r8e\n"
	"	x y M 4.4 10 RM r8e\n"
	"	x y M 2.9 4 RM r8e\n"
	"	x y M 1.4 -2 RM r8e\n"
	"	x y M -0.1 -8 RM r8e}!\n"

	/* dx dy dt - dot relative to head */
	"/dt{x y M RM currentpoint 1.2 0 360 arc fill}!\n"

	/* x y hld - fermata */
	"/hld{	1.5 add 2 copy 1.5 add M currentpoint 1.3 0 360 arc\n"
	"	M -7.5 0 RM\n"
	"	0 11.5 15 11.5 15 0 RC\n"
	"	-0.25 0 RL\n"
	"	-1.25 9 -13.25 9 -14.5 0 RC\n"
	"	fill}!\n"

	/* x y dnb - down bow */
	"/dnb{	dlw M -3.2 2 RM\n"
	"	0 7.2 RL\n"
	"	6.4 0 RM\n"
	"	0 -7.2 RL\n"
	"	currentpoint stroke M\n"
	"	-6.4 4.8 RM\n"
	"	0 2.4 RL\n"
	"	6.4 0 RL\n"
	"	0 -2.4 RL\n"
	"	fill}!\n"

	/* x y upb - up bow */
	"/upb{	dlw M -2.6 9.4 RM\n"
	"	2.6 -8.8 RL\n"
	"	2.6 8.8 RL\n"
	"	stroke}!\n"

	/* x y grm - gracing mark */
	"/grm{	M -5 2.5 RM\n"
	"	5 8.5 5.5 -4.5 10 2 RC\n"
	"	-5 -8.5 -5.5 4.5 -10 -2 RC fill}!\n"

	/* x y stc - staccato mark */
	"/stc{	3 add M currentpoint 1.2 0 360 arc fill}!\n"

	/* x y emb - emphasis bar */
	"/emb{	1.2 SLW 1 setlinecap M -2.5 3 RM 5 0 RL stroke 0 setlinecap}!\n"

	/* x y cpu - roll sign above head */
	"/cpu{	M -6 0 RM\n"
	"	0.4 7.3 11.3 7.3 11.7 0 RC\n"
	"	-1.3 6 -10.4 6 -11.7 0 RC fill}!\n"

	/* x y sld - slide */
	"/sld{	M -7.2 -4.8 RM\n"
	"	1.8 -0.7 4.5 0.2 7.2 4.8 RC\n"
	"	-2.1 -5 -5.4 -6.8 -7.6 -6 RC fill}!\n"

	/* x y trl - trill sign */
	"/trl{	/Times-BoldItalic 16 selectfont M -4 2 RM(tr)show}!\n"

	/* x y umrd - upper mordent */
	"/umrd{	4 add M\n"
	"	2.2 2.2 RL 2.1 -2.9 RL 0.7 0.7 RL\n"
	"	-2.2 -2.2 RL -2.1 2.9 RL -0.7 -0.7 RL\n"
	"	-2.2 -2.2 RL -2.1 2.9 RL -0.7 -0.7 RL\n"
	"	2.2 2.2 RL 2.1 -2.9 RL 0.7 0.7 RL fill}!\n"

	/* x y lmrd - lower mordent */
	"/lmrd{	2 copy umrd M .6 SLW 0 8 RL stroke}!\n"

	/* str x y fng - finger (0-5) */
	"/fng{/Bookman-Demi 8 selectfont M -3 1 RM show}!\n"

	/* str x y dacs - D.C. / D.S. */
	"/dacs{/Times-Roman 16 selectfont 3 add M showc}!\n"

	/* str x y crdc - italic annotations */
	"/crdc{/Times-Italic 14 selectfont 5 add M show}!\n"

	/* x y brth - breath */
	"/brth{/Times-BoldItalic 30 selectfont 6 add M(,)show}!\n"

	/* str x y pf - p, f, pp, .. */
	"/pf{/Times-BoldItalic 16 selectfont 5 add M show}!\n"

	/* str x y sfz */
	"/sfz{	M -7 5 RM pop\n"
	"	/Times-Italic 14 selectfont(s)show\n"
	"	/Times-BoldItalic 16 selectfont(f)show\n"
	"	/Times-Italic 14 selectfont(z)show}!\n"

	/* x y coda - coda */
	"/coda{	1 SLW 2 add 2 copy M 0 20 RL\n"
	"	2 copy M -10 10 RM 20 0 RL stroke\n"
	"	10 add 6 0 360 arc 1.7 SLW stroke}!\n"

	/* x y sgno - segno */
	"/sgno{	3 add M currentpoint currentpoint currentpoint\n"
	"	1.5 -1.7 6.4 0.3 3 3.7 RC\n"
	"	-10.4 7.8 -8 10.6 -6.5 11.9 RC\n"
	"	4 1.9 5.9 -1.7 4.2 -2.6 RC\n"
	"	-1.3 -0.7 -2.9 1.3 -0.7 2 RC\n"
	"	-1.5 1.7 -6.4 -0.3 -3 -3.7 RC\n"
	"	10.4 -7.8 8 -10.6 6.5 -11.9 RC\n"
	"	-4 -1.9 -5.9 1.7 -4.2 2.6 RC\n"
	"	1.3 0.7 2.9 -1.3 0.7 -2 RC\n"
	"	fill\n"
	"	M 0.8 SLW -6 1.2 RM 12.6 12.6 RL stroke\n"
	"	7 add exch 6 sub exch 1.2 0 360 arc fill\n"
	"	8 add exch 6 add exch 1.2 0 360 arc fill}!\n"

	/* w x y cresc - crescendo */
	"/cresc{	1 SLW M dup 5 RM\n"
	"	defl 1 and 0 eq\n"
	"	{dup neg 4 RL 4 RL}\n"
	"	{dup neg 2.2 RL 0 3.6 RM 2.2 RL}\n"
	"	ifelse stroke}!\n"

	/* w x y dim - diminuendo */
	"/dim{	1 SLW 5 add M\n"
	"	defl 2 and 0 eq\n"
	"	{dup 4 RL neg 4 RL}\n"
	"	{dup 2.2 RL 0 3.6 RM neg 2.2 RL}\n"
	"	ifelse stroke}!\n"

	/* x y dplus - plus */
	"/dplus{	1.2 SLW 0.5 add M 0 6 RL -3 -3 RM 6 0 RL stroke}!\n"

	/* x y accent - accent */
	"/accent{1.2 SLW M -4 1 RM 8 2 RL -8 2 RL stroke}!\n"

	/* x y marcato - accent */
	"/marcato{M -3 0 RM 3 7 RL 3 -7 RL -1.5 0 RL -1.8 4.2 RL -1.7 -4.2 RL fill}!\n"

	/* x y turn - turn */
	"/turn{	M 5.2 8 RM\n"
	"	1.4 -0.5 0.9 -4.8 -2.2 -2.8 RC\n"
	"	-4.8 3.5 RL\n"
	"	-3 2 -5.8 -1.8 -3.6 -4.4 RC\n"
	"	1 -1.1 2 -0.8 2.1 0.1 RC\n"
	"	0.1 0.9 -0.7 1.2 -1.9 0.6 RC\n"
	"	-1.4 0.5 -0.9 4.8 2.2 2.8 RC\n"
	"	4.8 -3.5 RL\n"
	"	3 -2 5.8 1.8 3.6 4.4 RC\n"
	"	-1 1.1 -2 0.8 -2.1 -0.1 RC\n"
	"	-0.1 -0.9 0.7 -1.2 1.9 -0.6 RC\n"
	"	fill}!\n"

	/* x y trnx - turn with line through it */
	"/turnx{	2 copy turn .6 SLW 1.5 add M 0 9 RL stroke}!\n"

	/* x y lphr - longphrase */
	"/lphr{1.2 SLW M 0 -18 RL stroke}!\n"

	/* x y mphr - mediumphrase */
	"/mphr{1.2 SLW M 0 -12 RL stroke}!\n"

	/* x y sphr - shortphrase */
	"/sphr{1.2 SLW M 0 -6 RL stroke}!\n"

	/* w x y ltr - long trill */
	"/ltr{	gsave 4 add T\n"
	"	0 6 3 -1 roll{\n"
	/*		% first loop draws left half of squiggle; second draws right\n*/
	"		2{\n"
	"			0 0.4 M\n"
	"			2 1.9 3.4 2.3 3.9 0 C\n"
	"			2.1 0 L\n"
	"			1.9 0.8 1.4 0.7 0 -0.4 C\n"
	"			fill\n"
	"			180 rotate -6 0 T\n"
	"		}repeat\n"
	/*		% shift axes right one squiggle*/
	"		pop 6 0 T\n"
	"	}for\n"
	"	grestore}!\n"

	/* h x ylow arp - arpeggio */
	"/arp{gsave 90 rotate exch neg ltr grestore}!\n"

	/* x y wedge - wedge */
	"/wedge{1 add M -1.5 5 RL 3 0 RL -1.5 -5 RL fill}!\n"

	/* x y opend - 'open' sign */
	"/opend{dlw M currentpoint 3 add 2.5 -90 270 arc stroke}!\n"

	/* x y snap - 'snap' sign */
	"/snap{	dlw 2 copy M -3 6 RM\n"
	"	0 5 6 5 6 0 RC\n"
	"	0 -5 -6 -5 -6 0 RC\n"
	"	5 add M 0 -6 RL stroke}!\n"

	/* x y thumb - 'thumb' sign */
	"/thumb{	dlw 2 copy M -2.5 7 RM\n"
	"	0 6 5 6 5 0 RC\n"
	"	0 -6 -5 -6 -5 0 RC\n"
	"	2 add M 0 -4 RL stroke}!\n"

	/* n x y trem - <n> tremolo on one note */
	"/trem{	M -4.5 0 RM{\n"
	"		currentpoint\n"
	"		9 3 RL 0 -3 RL -9 -3 RL 0 3 RL\n"
	"		fill 5.4 sub M\n"
	"	}repeat}!\n"

	/* x y hl - ledger line */
	"/hl{	.8 SLW M -6 0 RM 12 0 RL stroke}!\n"
	/* x y hl1 - longer ledger line */
	"/hl1{	.8 SLW M -7 0 RM 14 0 RL stroke}!\n"
	/* x y hl2 - more longer ledger line */
	"/hl2{	.7 SLW M -9 0 RM 18 0 RL stroke}!\n"

	/* -- accidentals -- */
	/* glyphs 1000 */
	"/usharp{<95200024\n"		/* width 460 */
	"	003cff42019a02ee\n"
	"	008802be\n"
	"	0088ff44\n"
	"	00a8ff44\n"
	"	00a802be\n"
	"	0128ff76\n"
	"	0148ff76\n"
	"	014802ee\n"
	"	012802ee\n"
	"	004001d0\n"
	"	0040015c\n"
	"	019201bc\n"
	"	01920230\n"
	"	00400076\n"
	"	00400002\n"
	"	01920064\n"
	"	019200d6\n"
	"	><0b000123030a0123030a0123030a0123030a>}cvlit def\n"
	"/uflat{<95200028\n"		/* width 400 */
	"	0064000001b802ee\n"
	"	006402ea\n"
	"	008402ea\n"
	"	0084000c\n"
	"	00640008\n"
	"	00840154\n"
	"	00b2019c011c01ae01540168\n"
	"	01b800fa00dc00220084000c\n"
	"	00840028\n"
	"	00ba0028014c00f60106014a\n"
	"	00d401860084014e00840128\n"
	"	><0b00010303030a0105050105050a>}cvlit def\n"
	"/unat{<95200022\n"		/* width 380 */
	"	003cff42013602ee\n"
	"	006002ee\n"
	"	004002ee\n"
	"	00400022\n"
	"	0060002a\n"
	"	01160060\n"
	"	0116ff46\n"
	"	0136ff46\n"
	"	01360208\n"
	"	011401fe\n"
	"	006001cc\n"
	"	006002ee\n"
	"	0060009e\n"
	"	0060015c\n"
	"	01160190\n"
	"	011600d4\n"
	"	><0b00012a030a0123030a>}cvlit def\n"
	"/udblesharp{<95200046\n"	/* width 460 */
	"	003c006e019001c2\n"
	"	00f0011a\n"
	"	01180140013a015e018e015e\n"
	"	018e01be\n"
	"	012e01be\n"
	"	012e016a0110014800ea0122\n"
	"	00c2014800a4016a00a401be\n"
	"	004401be\n"
	"	0044015e\n"
	"	009a015e00bc014000e2011a\n"
	"	00bc00f4009a00d6004400d6\n"
	"	00440076\n"
	"	00a40076\n"
	"	00a400ca00c200ec00ea0112\n"
	"	011000ec012e00ca012e0076\n"
	"	018e0076\n"
	"	018e00d6\n"
	"	013a00d6011800f400f0011a\n"
	"	><0b0001050303050503030505030305050303050a>}cvlit def\n"
	"/udbleflat{<9520004c\n"	/* width 500 */
	"	00140000022602ee\n"
	"	001402ea\n"
	"	002c02ea\n"
	"	002c000c\n"
	"	00140008\n"
	"	002c0154\n"
	"	004e019c009e01ae00c80168\n"
	"	011300fa00660022002c000c\n"
	"	002c0028\n"
	"	0054002800c200f6008d014a\n"
	"	00680186002c014e002c0128\n"
	"	010e02ea\n"
	"	012602ea\n"
	"	0126000c\n"
	"	010e0008\n"
	"	01260154\n"
	"	0148019c019801ae01c20168\n"
	"	020d00fa016000220126000c\n"
	"	01260028\n"
	"	014e002801bc00f60187014a\n"
	"	016201860126014e01260128\n"
	"	><0b000123030a0105050105050a0123030a0105050105050a>}cvlit def\n"

	/* x y sh0 - sharp sign */
	"/sh0{	gsave T -4 -5 T .018 dup scale usharp ufill grestore}!\n"
	/* x y ft0 - flat sign */
	"/ft0{	gsave T -3.5 -3.5 T .018 dup scale uflat ufill grestore}!\n"
	/* x y nt0 - natural sign */
	"/nt0{	gsave T -3 -5 T .018 dup scale unat ufill grestore}!\n"
	/* x y dsh0 - double sharp */
	"/dsh0{	gsave T -4 -5 T .018 dup scale udblesharp ufill grestore}!\n"
	/* x y dft0 - double flat sign */
	"/dft0{	gsave T -4 -3.5 T .018 dup scale udbleflat ufill grestore}!\n"
	/* ancillary function for grace note accidentals */
	"/gsc{gsave y T .7 dup scale 0 0}!\n"

	/* some microtone accidentals */
	/* 1/4 ton sharp */
	"/sh1{	gsave T .9 SLW\n"
	"	0 -7.8 M 0 15.4 RL stroke\n"
	"	-1.8 -2.7 M 3.6 1.1 RL 0 -2.2 RL -3.6 -1.1 RL 0 2.2 RL fill\n"
	"	-1.8 3.7 M 3.6 1.1 RL 0 -2.2 RL -3.6 -1.1 RL 0 2.2 RL fill\n"
	"	grestore}!\n"
	/* 3/4 ton sharp */
	"/sh513{	gsave T .8 SLW\n"
	"	-2.5 -8.7 M 0 15.4 RL\n"
	"	0 -7.8 M 0 15.4 RL\n"
	"	2.5 -6.9 M 0 15.4 RL stroke\n"
	"	-3.7 -3.1 M 7.4 2.2 RL 0 -2.2 RL -7.4 -2.2 RL 0 2.2 RL fill\n"
	"	-3.7 3.2 M 7.4 2.2 RL 0 -2.2 RL -7.4 -2.2 RL 0 2.2 RL fill\n"
	"	grestore}!\n"
	/* 1/4 ton flat */
	"/ft1{gsave -1 1 scale exch neg exch ft0 grestore}!\n"
	/* x y ftx - narrow flat sign */
	"/ftx{	-1.4 2.7 RM\n"
	"	5.7 3.1 5.7 -3.6 0 -6.7 RC\n"
	"	3.9 4 4 7.6 0 5.8 RC\n"
	"	currentpoint fill 7.1 add M\n"
	"	dlw 0 -12.4 RL stroke}!\n"
	/* 3/4 ton flat */
	"/ft513{2 copy gsave -1 1 scale exch neg 3 add exch M ftx grestore\n"
	"	M 1.5 0 RM ftx}!\n"
	/* microscale= 4 */
	"/sh4tb[/.notdef/sh1/sh0/sh513/dsh0]def\n"
	"/sh4{sh4tb exch get cvx exec}!\n"
	"/ft4tb[/.notdef/ft1/ft0/ft513/dft0]def\n"
	"/ft4{ft4tb exch get cvx exec}!\n"

	/* str gcshow - guitar chord */
	"/gcshow{show}!\n"
	/* x y w h box - draw a box */
	"/box{.6 SLW rectstroke}!\n"
	/* set the start of a guitar chord in a box */
	"/boxstart{currentpoint pop/x exch def}!\n"
	/* mark the end of a guitar chord in a box */
	"/boxmark{currentpoint pop dup x gt\n"
	"	{/x exch def}{pop}ifelse}!\n"
	/* x y dy boxdraw - draw a box around a guitar chord */
	"/boxdraw{x 3 index sub 2 add exch box}!\n"
	/* w str gxshow - expand a guitar chord */
	"/gxshow{0 9 3 -1 roll widthshow}!\n"

	/* str anshow - annotation */
	"/anshow{show}!\n"

	/* -- lyrics under notes -- */
	/* w x y wln - underscore line */
	"/wln{M .8 SLW 0 RL stroke}!\n"
	/* w x y hyph - hyphen */
	"/hyph{	.8 SLW 3 add M\n"
	"	dup cvi 20 idiv 3 mul 25 add\n"	/* w d */
	"	1 index cvi exch idiv 1 add "	/* w n */
		"exch "				/* n w */
		"1 index div\n"			/* n dx */
	"	dup 4 sub "			/* n dx (dx-4) */
		"3 1 roll "			/* (dx-4) n dx */
		".5 mul 2 sub 0 RM\n"		/* (dx / 2 - 4) rmoveto */
	"	{4 0 RL dup 0 RM}repeat stroke pop}!\n"
	/* str lyshow - lyrics */
	"/lyshow{show}!\n"

	/* -- bars -- */
	/* h x y bar - thin bar */
	"/bar{M dlw 0 exch RL stroke}!\n"
	/* h x y dotbar - dotted bar */
	"/dotbar{[5] 0 setdash bar [] 0 setdash}!\n"
	/* h x y thbar - thick bar */
	"/thbar{3 -1 roll 3 exch rectfill}!\n"
	/* x y rdots - repeat dots */
	"/rdots{	2 copy 9 add M currentpoint 1.2 0 360 arc\n"
	"	15 add M currentpoint 1.2 0 360 arc fill}!\n"

	/* x y csig - C timesig */
	"/csig{	M\n"
	"	1 17.3 RM\n"
	"	0.9 0 2.3 -0.7 2.4 -2.2 RC\n"
	"	-1.2 2 -3.6 -0.1 -1.6 -1.7 RC\n"
	"	2 -1 3.8 3.5 -0.8 4.7 RC\n"
	"	-2 0.4 -6.4 -1.3 -5.8 -7 RC\n"
	"	0.4 -6.4 7.9 -6.8 9.1 -0.7 RC\n"
	"	-2.3 -5.6 -6.7 -5.1 -6.8 0 RC\n"
	"	-0.5 4.4 0.7 7.5 3.5 6.9 RC\n"
	"	fill}!\n"

	/* x y ctsig - C| timesig */
	"/ctsig{dlw 2 copy csig 4 add M 0 16 RL stroke}!\n"

	/* x y xxsig - old time signatures ('o', 'o.', 'c' 'c.') */
	"/pmsig{0.3 SLW 12 add M currentpoint 5 0 360 arc stroke}!\n"
	"/pMsig{2 copy pmsig 12 add M currentpoint 1.3 0 360 arc fill}!\n"
	"/imsig{0.3 SLW 12 add 2 copy 5 add M 5 90 270 arc stroke}!\n"
	"/iMsig{2 copy imsig 12 add M currentpoint 1.3 0 360 arc fill}!\n"

	/* (top) (bot) x y tsig - time signature */
	"/tsig{	1 add M gsave/Times-Bold 16 selectfont 1.2 1 scale\n"
	"	currentpoint 3 -1 roll showc\n"
	"	12 add M showc grestore}!\n"

	/* (meter) x y stsig - single time signature */
	"/stsig{	7 add M gsave/Times-Bold 18 selectfont 1.2 1 scale\n"
	"	showc grestore}!\n"

	/* width n x y staff - staff with n lines*/
	"/staff{	dlw M{dup 0 RL dup neg 6 RM}repeat\n"
	"	pop stroke}!\n"

	/* l x sep0 - hline separator */
	"/sep0{	dlw 0 M 0 RL stroke}!\n"

	"/hbrce{	-2.5 1 RM\n"
	"	-4.5 -4.6 -7.5 -12.2 -4.4 -26.8 RC\n"
	"	3.5 -14.3 3.2 -21.7 -2.1 -24.2 RC\n"
	"	7.4 2.4 7.3 14.2 3.5 29.5 RC\n"
	"	-2.7 9.5 -1.5 16.2 3 21.5 RC\n"
	"	fill}!\n"
	/* h x y brace */
	"/brace{	gsave T 0 0 M .01 mul 1 exch scale hbrce\n"
	"	0 -100 M 1 -1 scale hbrce grestore}!\n"

	/* h x y bracket */
	"/bracket{M -5 2 RM currentpoint\n"
	"	-1.7 2 RM 10.5 -1 12 4.5 12 3.5 RC\n"
	"	0 -1 -3.5 -5.5 -8.5 -5.5 RC fill\n"
	"	3 SLW 2 add M\n"
	"	0 exch neg 8 sub RL currentpoint stroke\n"
	"	M -1.7 0 RM\n"
	"	10.5 1 12 -4.5 12 -3.5 RC\n"
	"	0 1 -3.5 5.5 -8.5 5.5 RC fill}!\n"

	/* nb_measures x y mrest */
	"/mrest{	gsave T 1 SLW\n"
	"	-20 6 M 0 12 RL 20 6 M 0 12 RL stroke\n"
	"	5 SLW -20 12 M 40 0 RL stroke\n"
	"	/Times-Bold 15 selectfont 0 28 M showc grestore}!\n"

	/* x y mrep - measure repeat */
	"/mrep{	2 copy 2 copy\n"
	"	M -5 15 RM currentpoint 1.4 0 360 arc\n"
	"	M 5 9 RM currentpoint 1.4 0 360 arc\n"
	"	M -7 6 RM 11 12 RL 3 0 RL -11 -12 RL -3 0 RL fill}!\n"

	/* x y mrep2 - measure repeat 2 times */
	"/mrep2{	2 copy 2 copy\n"
	"	M -5 18 RM currentpoint 1.4 0 360 arc\n"
	"	M 5 6 RM currentpoint 1.4 0 360 arc fill\n"
	"	M 1.8 SLW\n"
	"	-7 4 RM 14 10 RL -14 -4 RM 14 10 RL stroke}!\n"

	/* x y srep - sequence repeat */
	"/srep{	M -1 6 RM 11 12 RL 3 0 RL -11 -12 RL -3 0 RL fill}!\n"

	/* str dy bracket_type dx x y repbra - repeat bracket */
	"/repbra{gsave dlw T 0 -20 M\n"
	"	0 20 3 index 1 ne{RL}{RM}ifelse 0 RL 0 ne{0 -20 RL}if stroke\n"
	"	4 exch M show grestore}!\n"

	/* pp2x pp1x p1 pp1 pp2 p2 p1 SL - slur / tie */
	"/SL{M RC RL RC closepath fill}!\n"

	/* pp2x pp1x p1 pp1 pp2 p2 p1 dSL - dotted slur / tie */
	"/dSL{	M [4] 0 setdash .8 SLW RC stroke [] 0 setdash}!\n"

	/* -- text -- */
	"/strw{	gsave 0 1000 M/strop/show load def 1 setgray str\n"
	"	0 setgray currentpoint pop/w exch def grestore}!\n"
	"/jshow{w 0 32 4 -1 roll widthshow}!\n"
	"/strop/show load def\n"
	"/arrayshow{{dup type/stringtype eq{strop}{glyphshow}ifelse}forall}def\n"

	/* -- note heads -- */
	/* x y hd - full head */
	"/uhd{{	100 -270 640 280\n"
	"	560 82\n"
	"	474 267 105 105 186 -80\n"
	"	267 -265 636 -102 555 82\n"
	"	}<0b000122050a>}cvlit def\n"
	"/hd{	/x 2 index def/y 1 index def\n"
	"	gsave T -7.4 0 T .02 dup scale uhd ufill grestore}!\n"
	/* x y Hd - open head for half */
	"/Hd{	xymove\n"
	"	3 1.6 RM\n"
	"	-1 1.8 -7 -1.4 -6 -3.2 RC\n"
	"	1 -1.8 7 1.4 6 3.2 RC\n"
	"	0.5 0.3 RM\n"
	"	2 -3.8 -5 -7.6 -7 -3.8 RC\n"
	"	-2 3.8 5 7.6 7 3.8 RC fill}!\n"
	/* x y HD - open head for whole */
	"/HD{	xymove\n"
	"	-2.7 1.4 RM\n"
	"	1.5 2.8 6.9 0 5.3 -2.7 RC\n"
	"	-1.5 -2.8 -6.9 0 -5.3 2.7 RC\n"
	"	8.3 -1.4 RM\n"
	"	0 1.5 -2.2 3 -5.6 3 RC\n"
	"	-3.4 0 -5.6 -1.5 -5.6 -3 RC\n"
	"	0 -1.5 2.2 -3 5.6 -3 RC\n"
	"	3.4 0 5.6 1.5 5.6 3 RC fill}!\n"
	/* x y HDD - round breve */
	"/HDD{	dlw HD\n"
	"	x y M -6 -4 RM 0 8 RL\n"
	"	12 0 RM 0 -8 RL stroke}!\n"
	/* x y breve - square breve */
	"/breve{	xymove\n"
	"	2.5 SLW -6 -2.7 RM 12 0 RL\n"
	"	0 5.4 RM -12 0 RL stroke\n"
	"	dlw x y M -6 -5 RM 0 10 RL\n"
	"	12 0 RM 0 -10 RL stroke}!\n"
	/* x y longa */
	"/longa{	xymove\n"
	"	2.5 SLW -6 -2.7 RM 12 0 RL\n"
	"	0 5.4 RM -12 0 RL stroke\n"
	"	dlw x y M -6 -5 RM 0 10 RL\n"
	"	12 0 RM 0 -16 RL stroke}!\n"

	/* -- default percussion heads -- */
	/* x y pshhd - percussion sharp head */
	"/pshhd{/x 2 index def/y 1 index def dsh0}!\n"
	/* x y pfthd - percussion flat head */
	"/pfthd{/x 2 index def/y 1 index def dsh0\n"
	"	.7 SLW x y 4 0 360 arc stroke}!\n"
	/* same for dble sharp/flat */
	"/pdshhd{pshhd}!\n"
	"/pdfthd{pfthd}!\n"

	/* x y ghd - grace note head */
	"/ghd{	xymove\n"
	"	1.7 1 RM\n"
	"	-1 1.7 -4.5 -0.2 -3.4 -2 RC\n"
	"	1 -1.7 4.5 0.2 3.4 2 RC fill}!\n"

	/* dx dy gua / gda - acciaccatura */
	"/gua{x y M -1 4 RM RL stroke}!\n"
	"/gda{x y M -5 -4 RM RL stroke}!\n"

	/* x y ghl - grace note ledger line */
	"/ghl{	.6 SLW M -3 0 RM 6 0 RL stroke}!\n"

	/* x1 y2 x2 y2 x3 y3 x0 y0 gsl - grace note slur */
	"/gsl{dlw M RC stroke}!\n"

	/* x y custos */
	"/custos{2 copy M -4 0 RM 2 2.5 RL 2 -2.5 RL 2 2.5 RL 2 -2.5 RL\n"
	"	-2 -2.5 RL -2 2.5 RL -2 -2.5 RL -2 2.5 RL fill\n"
	"	M 3.5 0 RM 5 7 RL dlw stroke}!\n"

#ifdef HAVE_PANGO
	"/glypharray{{glyphshow}forall}!\n"
#endif

	/* x y showerror */
	"/showerror{gsave 1 0.7 0.7 setrgbcolor 2.5 SLW newpath\n"
	"	30 0 360 arc stroke grestore}!\n"

	"/pdfmark where{pop}{userdict/pdfmark/cleartomark load put}ifelse\n"

	"0 setlinecap 0 setlinejoin\n";

/* -- define a font -- */
void define_font(char name[],
		 int num,
		 int enc)
{
	if (enc == 0)		/* utf-8 */
		fprintf(fout, "/%s-utf8/%s mkfont\n"
			"/F%d{/%s-utf8 exch selectfont}!\n",
			name, name, num, name);
	else			/* native encoding */
		fprintf(fout, "/F%d{/%s exch selectfont}!\n", num, name);
}

/* -- output the symbol definitions -- */
void define_symbols(void)
{
	fputs(ps_head, fout);

	/* len su - up stem */
	fprintf(fout, "/su{dlw x y M %.1f %.1f RM %.1f sub 0 exch RL stroke}!\n",
		STEM_XOFF, STEM_YOFF, STEM_YOFF);

	/* len sd - down stem */
	fprintf(fout, "/sd{dlw x y M %.1f %.1f RM %.1f add 0 exch RL stroke}!\n",
		-STEM_XOFF, -STEM_YOFF, STEM_YOFF);

	/* n len sfu - stem and n flags up */
	fprintf(fout, "/sfu{	dlw x y M %.1f %.1f RM\n"
		"	%.1f sub 0 exch RL currentpoint stroke\n"
		"	M dup 1 eq{\n"
		"		pop\n"
		"		0.6 -5.6 9.6 -9 5.6 -18.4 RC\n"
		"		1.6 6 -1.3 11.6 -5.6 12.8 RC fill\n"
		"	  }{\n"
		"		1 sub{	currentpoint\n"
		"			0.9 -3.7 9.1 -6.4 6 -12.4 RC\n"
		"			1 5.4 -4.2 8.4 -6 8.4 RC\n"
		"			fill 5.4 sub M\n"
		"		}repeat\n"
		"		1.2 -3.2 9.6 -5.7 5.6 -14.6 RC\n"
		"		1.6 5.4 -1 10.2 -5.6 11.4 RC fill\n"
		"	  }ifelse}!\n",
		STEM_XOFF, STEM_YOFF, STEM_YOFF);

	/* n len sfd - stem and n flags down */
	fprintf(fout, "/sfd{	dlw x y M %.1f %.1f RM\n"
		"	%.1f add 0 exch RL currentpoint stroke\n"
		"	M dup 1 eq{\n"
		"		pop\n"
		"		0.6 5.6 9.6 9 5.6 18.4 RC\n"
		"		1.6 -6 -1.3 -11.6 -5.6 -12.8 RC fill\n"
		"	  }{\n"
		"		1 sub{	currentpoint\n"
		"			0.9 3.7 9.1 6.4 6 12.4 RC\n"
		"			1 -5.4 -4.2 -8.4 -6 -8.4 RC\n"
		"			fill 5.4 add M\n"
		"		}repeat\n"
		"		1.2 3.2 9.6 5.7 5.6 14.6 RC\n"
		"		1.6 -5.4 -1 -10.2 -5.6 -11.4 RC fill\n"
		"	  }ifelse}!\n",
		-STEM_XOFF, -STEM_YOFF, STEM_YOFF);

	/* n len sfs - stem and n straight flag down */
	fprintf(fout, "/sfs{	dup 0 lt{\n"
		"		dlw x y M -%.1f -%.1f RM\n"
		"		%.1f add 0 exch RL currentpoint stroke\n"
		"		M{	currentpoint\n"
		"			7 %.1f RL\n"
		"			0 %.1f RL\n"
		"			-7 -%.1f RL\n"
		"			fill 5.4 add M\n"
		"		}repeat\n"
		"	}{\n"
		"		dlw x y M %.1f %.1f RM\n"
		"		%.1f sub 0 exch RL currentpoint stroke\n"
		"		M{	currentpoint\n"
		"			7 -%.1f RL\n"
		"			0 -%.1f RL\n"
		"			-7 %.1f RL\n"
		"			fill 5.4 sub M\n"
		"		}repeat\n"
		"	}ifelse}!\n",
		STEM_XOFF, STEM_YOFF, STEM_YOFF,
		BEAM_DEPTH, BEAM_DEPTH, BEAM_DEPTH,
		STEM_XOFF, STEM_YOFF, STEM_YOFF,
		BEAM_DEPTH, BEAM_DEPTH, BEAM_DEPTH);

	/* len gu - grace note stem up */
	fprintf(fout, "/gu{	.6 SLW x y M\n"
		"	%.1f 0 RM 0 exch RL stroke}!\n"

	/* len gd - grace note stem down */
		"/gd{	.6 SLW x y M\n"
		"	%.1f 0 RM 0 exch RL stroke}!\n",
		GSTEM_XOFF, -GSTEM_XOFF);

	/* n len sgu - gnote stem and n flag up */
	fprintf(fout, "/sgu{	.6 SLW x y M %.1f 0 RM\n"
		"	0 exch RL currentpoint stroke\n"
		"	M dup 1 eq{\n"
		"		pop\n"
		"		0.6 -3.4 5.6 -3.8 3 -10 RC\n"
		"		1.2 4.4 -1.4 7 -3 7 RC fill\n"
		"	  }{\n"
		"		{	currentpoint\n"
		"			1 -3.2 5.6 -2.8 3.2 -8 RC\n"
		"			1.4 4.8 -2.4 5.4 -3.2 5.2 RC\n"
		"			fill 3.5 sub M\n"
		"		}repeat\n"
		"	  }ifelse}!\n",
		GSTEM_XOFF);

	/* n len sgd - gnote stem and n flag down */
	fprintf(fout, "/sgd{	.6 SLW x y M %.1f 0 RM\n"
		"	0 exch RL currentpoint stroke\n"
		"	M dup 1 eq{\n"
		"		pop\n"
		"		0.6 3.4 5.6 3.8 3 10 RC\n"
		"		1.2 -4.4 -1.4 -7 -3 -7 RC fill\n"
		"	  }{\n"
		"		{	currentpoint\n"
		"			1 3.2 5.6 2.8 3.2 8 RC\n"
		"			1.4 -4.8 -2.4 -5.4 -3.2 -5.2 RC\n"
		"			fill 3.5 add M\n"
		"		}repeat\n"
		"	  }ifelse}!\n",
		-GSTEM_XOFF);

	/* n len sgs - gnote stem and n straight flag up */
	fprintf(fout, "/sgs{	.6 SLW x y M %.1f 0 RM\n"
		"	0 exch RL currentpoint stroke\n"
		"	M{	currentpoint\n"
		"		3 -1.5 RL 0 -2 RL -3 1.5 RL\n"
		"		closepath fill 3 sub M\n"
		"	}repeat}!\n",
		GSTEM_XOFF);
}
abcm2ps-8.5.2/voices.abc0000644000175000017500000000575611712024557013127 0ustar  jefjef% here are only some first lines of sample tunes

%%deco fp 6 pf 20 2 5 fp

% --- piano ---

X:1
T:Song for a guy
C:Elton John
M:C
L:1/8
Q:1/4=128
%%staves {RH LH}
K:C
% most of piano tunes have only 2 voices ..
V:RH
[C8E8]|zE FG- GEC2|[B,3E3][B,D]- [B,4D4]|zD EF- FED2|D8|
V:LH
[C,3G,3][C,G,]- [C,4G,4]|[C,3G,3][C,G,]- [C,4G,4]|[B,,3G,3][B,,G,]- [B,,4G,4]|\
[B,,3G,3][B,,G,]- [B,,4G,4]|[_B,,3F,3][B,,F,]- [B,,4F,4]|

X:2
T:8th Sonata for piano
C:L. van Beethoven
M:C
L:1/16
Q:1/8=66
%%staves {1 2}
K:Cm
% .. even when there are a lot of notes
V:1
!fp![E,4G,4C4]- [E,3/G,3/C3/]!3![G,/C/]!4![G,3/=B,3/D3/]!5![G,/C/E/] ([=A,4C4E4]!4![=B,2D2])z2|\
!fp!!3![=B,4D4F4]- [B,3/D3/F3/][B,/D/F/][B,3/D3/G3/][B,/D/A/] ([B,4D4A4]!3![C2E2G2])z2|
V:2
[C,,4E,,4G,,4C,4]- [C,,3/E,,3/G,,3/C,3/]!2!E,/!3!D,3/!4!C,/ (!2!^F,4G,2)z _A,,|\
_A,4-A,3/!2!A,/!1!G,3/=F,/ E,4-E,2z3/ E,/|

X:3
T: Praeludium II (WT II)
C: J.S. Bach
M: C
L: 1/16
Q:1/4=66
%%staves {RH LH}
%%MIDI program 6
K:Cm
% same as bach.abc (abc2ps-1.3.0) but rewritten in a more standard way
V:RH
  zGFG AFEF GEDE FDCD     | E2c2F2c2 E2c2D2=B2                  | 
V:LH
  C,2C2F,2C2 E,2C2D,2=B,2 | C,G,F,G, A,F,E,F, G,E,D,E, F,D,C,D, |  

X:4
T:Allegro grazioso
C:Schumann
M:C
L:1/8
Q:1/4=66
%%staves {1 (2 3)}
K:G
V:1
(!p!B2AB G3)(A |Bcd[Ge]) ([F3A3][^GB])|([A2c2][^GB][Ac] AF=GA)|[G2B2][F2A2] {/G}G3B|
V:2
% this voice is not complete, but the measure must be respected
 G,2C2 xD3-    |D2x2	 x4	      | A,2D2		C2x2  |x8		   |
V:3
(G,DCD B,D2)(F,|G,A,B,C  (D2)CB,)     |(A,EDE		CDB,C)|D2[D,2C2]  [G,3B,3]D|

% --- vocal ---

X:5
T:Tridal a ra va c'halon
T:(Mor Fawr Wyt Ti!)
M:4/4
L:1/8
Q:1/4=48
%%staves [(S A) (T B)]
K:A
% Breton words on a Wales choral
V:S
EEE	  |C3E	   EEFF        |(D2F3)	  FFF	 |E3C   EEDD		 |C4   z:|
w:Ka-na a |rin, ka-na a rin be-|pred,* rag an Ao-|trou en-eus va zan-tel-|let.
w:Eñ eo va|nerz, eñ eo i-vez va|Zad,*    eñ eo va|han, ka-na a rin e     |hloar.
V:A
CCC	  |A,3A,   B,CA,A,     |(B,2A,3)  DDB,   |C3A,  B,A,A,G,	 |A,4  z:|
V:T
A,A,A,	  |E,3E,   G,A,F,F,    |(F,2F,3)  A,A,G, |A,3A, G,A,F,E,	 |E,4  z:|
V:B
A,,A,,A,, |A,,3C,  B,,A,,D,D,  |(B,,2D,3) D,D,B,,|E,3F, E,C,B,,E,,	 |A,,4 z:|

% --- organ ---

X:6
T:Wär Gott nicht mit uns diese Zeit
C:Johann Nicolaus Hanff
M:C
L:1/8
Q:1/4=66
%%staves [1 (2 3) 4]
V:1 nm="Rückpos"
V:2 nm="Organo"
K:Am
V:1
%%MIDI program 53
A3B    c2c2    |d2e2    de/f/P ^c3/d/|d8    |z8		  |
V:2
%%MIDI program 73
z2E2-  E2AG    |F2E2    F2E2	     |F6  F2|E2CD   E3F/G/|
V:3
%%MIDI program 73
z2C2-  CB,A,2  |A,8		     |A,6 D2|C2A,B, C3D/E/|
V:4
%%MIDI program 73
z2A,2- A,G,F,E,|D,2^C,2 D,2A,,2      |D,,8  |z4	    z2A,2 |

X:7
T:Qui Tolis (Trio)
C:André Raison
M:3/4
L:1/4
Q:1/4=92
%%staves {(Pos1 Pos2) Trompette}
K:F
%
V:Pos1
%%MIDI program 78
"^Positif"x3  |x3    |c'>ba|Pga/g/f|:g2a  |ba2    |g2c- |c2P=B  |c>de  |fga    |
V:Pos2
%%MIDI program 78
	 Mf>ed|cd/c/B|PA2d |ef/e/d |:e2f  |ef2    |c>BA |GA/G/F |E>FG  |ABc-   |
V:Trompette
%%MIDI program 56
"^Trompette"z3|z3    |z3   |z3     |:Mc>BA|PGA/G/F|PE>EF|PEF/E/D|C>CPB,|A,G,F,-|

bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped)
Email: contact@elmoujehidin.net bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped) Email: contact@elmoujehidin.net