/*  
 *  This file is part of abc2ps, Copyright (C) 1996,1997 Michael Methfessel
 *  See file abc2ps.c for details.
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#ifdef _MSVC
#include <process.h>
#endif
#include "abc2ps.h" 

#define STYLE "std"
#define MAXNTEXT	400	/* for text output */
#define MAXWLEN		21

static int music_only;		/* no vocals if 1 */
static int pretty;			/* for pretty but sprawling layout */
static int bars_per_line;	/* bars for auto linebreaking */
static int continue_lines;	/* flag to continue all lines */
static int landscape;		/* flag for landscape output */
static float lmargin;		/* left margin */
static float swidth;		/* staff width */
static float scalefac; 		/* scale factor for symbol size */
static float alfa_c;		/* max compression allowed */
static float staffsep; 		/* staff separation */
static char outfnam[STRL1];	/* internal file name for open/close */
static char styf[STRL1];	/* layout style file name */
static char styd[STRL1];	/* layout style directory */
static char txt[MAXNTEXT][MAXWLEN];	/* for output of text */
int  ntxt;

extern char *p;				/* global pointers for parsing music line */
extern char *p0;			/* beginning of line */
static int PNTop;			/* [JSA] */
static int PMirror;			/* [JSA] */
int FirstPgNum = 0;			/* [JSA] */
int ParamUnits = 0;
 
/*  miscellaneous subroutines  */

/* ----- write_help ----- */
void write_help()
{
	printf("Usage: jaabc2ps files..  [-e nums-or-pats] [other flags]\n"
		"  - show index of abc files or typeset tunes in Postscript.\n"
		"where: files   input files in abc format\n"
		"       nums    tune xref numbers, i.e. 1 3 6-7,20-\n"
		"       pats    patterns for title search\n" 
		"Tunes are selected if they match a number or a pattern.\n"
		"Flags: -o      write output for selected tunes\n"
		"       -E      produce EPSF output, one tune per file\n"
		"       -O aa   set outfile name to aaa\n"
		"       -O =    make outfile name from infile/title\n" 
		"       -i      run in interactive mode\n"
		"       -v nn   set verbosity level to nn\n"
		"       -h      show this command summary\n"
		"  Selection:\n"
		"       -e      following arguments are selectors\n"
		"       -f      following arguments are file names\n"
		"       -C      search Composer field instead of title\n"
		"       -R      search  Rhythm  field instead of title\n"
		"       -S      search  Source  field instead of title\n"
		"       -A      select all tunes (override other selectors)\n"
		"       -V n    deselect voice # \"n\" (cumulative)\n"
		"  Formatting:\n"
		"       -H [cm|in|pt] show the format parameters (default is inch)\n"
		"       -p      pretty output (looks better, needs more space)\n"
		"       -P      select second predefined pretty output style\n"
		"       -s xx   set scale factor for symbol size 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"
		"       -x      include xref numbers in output\n"
		"       -n      include notes and history in output\n"
		"       -N      write page numbers\n"
		"       -1      write one tune per page\n"
		"       -l      landscape mode\n"
		"       -L      fix line breaks in source files\n"
		"       -g xx   set glue mode to shrink|space|stretch|fill\n"
		"       -F foo  read format from \"foo.fmt\"\n"
		"       -D bar  look for format files in directory \"bar\"\n"
		"       -j n    Number the measures every n bars or on the left if 0\n"
		"       -# n    Start page numbering at \"n.\"\n"
		"  Tablature:\n"
		"       -Wvk    Print whistle tablature for voice \"v\" and a\n"
		"               whistle in the key of \"k\" (ex: \"-W1Bb\" or \"-W3LD\").\n"
		"               May be used for up to four voices.  Whistle\n"
		"               tablature appears below the staves.\n"
		"       -Tv<C|D[+]><string pitches>[capo fret][:simple|marked]\n"
		"               Print string tablature for voice \"v.\" Instrument\n"
		"               is <C>hromatic or mountain <D>ulcimer.  The \"+\" means\n"
		"               that the dulcimer has the 6-1/2 fret.  The string pitches\n"
		"               give the tuning for each string (ex. \"^d'\").  Up to\n"
		"               eight strings are permitted.  Optionally a fret may\n"
		"               be specified as the capo location.  Also optionally\n"
		"               a style may be specified.  The default style is simple\n"
		"               meaning that only specific notes as supplied in the\n"
		"               voice will be tabbed.  \"Marked\" means that open strings\n"
		"               will be explicitly tabbed whenever an upbow or downbow\n"
		"               is encountered.\n"
		"  Line breaks:\n"
		"       -a xx   set max shrinkage to xx (between 0 and 1)\n"
		"       -b      break at all line ends (ignore continuations)\n"
		"       -c      continue all line ends (append '\\')\n"
		"       -B bb   put line break every bb bars\n");
}

/* ----- is_xrefstr: check if string ok for xref selection ---- */
int is_xrefstr(char str[])
{
	char *c;

	c=str;
	while (*c != '\0') {
		if (!isdigit(*c) && *c!='-' && *c!=',' && *c!=' ')
			return 0;
		c++;
	}
	return 1;
}

/* ----- make_arglist: splits one string into list or arguments ---- */
int make_arglist(char str[],
		 char *av[])
{
	char *q;
	int n;

	q=str;
	n=1;			/* first real arg is 1, as in argv */
	for (;;) {
		while (*q==' ')
			q++;
		if (*q=='\0')
			break;
		av[n]=q;
		n++;
		while (*q!=' ' && *q!='\0')
			q++;
		if (*q=='\0')
			break;
		*q='\0';
		q++;
	}
	return n;
}

/* ----- init_ops ----- */
void init_ops(int job)
{
	one_per_page	= -1;
	landscape	= -1;
	scalefac	= -1.0;
	lmargin		= -1.0;
	swidth		= -1.0;
	write_history   = -1;
	staffsep	= -1;
	break_continues = -1;
	continue_lines  = -1;
	include_xrefs   = -1;
	music_only	= -1;
	alfa_c		= -1.0;
	PNTop = PMirror = -1;

	select_all	= 0;
	do_mode		= DO_INDEX;
	pagenumbers	= 0;
	styf[0] = '\0';

	if (job) {
		strcpy(styd, DEFAULT_FDIR);
		strcpy(outf, OUTPUTFILE);
		interactive	= 0;
		pretty		= 0;
		epsf		= 0;
		choose_outname	= 0;
		gmode		= G_FILL;
		vb		= VERBOSE0;
		search_field0	= S_TITLE;
	}
}

/* ----- ops_into_fmt ----- */
void ops_into_fmt(struct FORMAT *fmt)
{
	if (landscape >= 0)
		fmt->landscape=landscape;
	if (scalefac >= 0)
		fmt->scale=scalefac;
	if (lmargin >= 0)
		fmt->leftmargin=lmargin;
	if (swidth >= 0)
		fmt->staffwidth=swidth;
	if (continue_lines >= 0)
		fmt->continueall=continue_lines;
	if (break_continues >= 0)
		fmt->breakall=break_continues;
	if (write_history >= 0)
		fmt->writehistory=write_history;
	if (bars_per_line >= 0)
		fmt->barsperstaff=bars_per_line;
	if (include_xrefs >= 0)
		fmt->withxrefs=include_xrefs;
	if (staffsep >= 0)
		fmt->staffsep=staffsep;
	if (one_per_page >= 0)
		fmt->oneperpage=one_per_page;
	if (music_only >= 0)
		fmt->musiconly=music_only;
	if (alfa_c >= 0)
		fmt->maxshrink=alfa_c;
	if (PNTop >= 0) fmt->PageNumTop = PNTop;
	if (PMirror >= 0) fmt->PageMirror = PMirror;
}

/* ----- parse_args: parse list of arguments, interpret flags ----- */
int parse_args(int ac,
	       char *av[])
{
	int i,m,k,nsel,sel_arg,j,ok,f_pos,got_value;
	char c,aaa[201],ext[41];
	int	w_voice, whistle_pitch;		/* [JSA] */
	int tab_voice;					/* [JSA] */

	help_me=0;
	ninf=0;
	nsel=0;
	sel_arg=0;
	f_pos=-1;
	sel_str[0][0] = '\0';
	s_field[0]=search_field0;
	for (i = 0; i < MAXVOICE; i++) VoiceMute_tb[i] = FALSE; /* [JSA] */
	FixEols = FALSE;

	for (i = 1; i < ac; i++) {
		if (av[i][0]=='+') {	/* switch off flags with '+' */
			m=1;
			k=strlen(av[i]);
			while (m<k) {
				switch (av[i][m]) {
				case 'A': select_all=0; break;
				case 'B': bars_per_line=0; break;
				case 'b': break_continues=0; break;
				case 'c': continue_lines=0; break;
				case 'E': epsf=0; break;
				case 'F': strcpy(styf, ""); break;
				case 'i': interactive=0; break;
				case 'l': landscape=0; break;
				case 'L': FixEols = FALSE; break;
				case 'M': music_only=0; break;
				case 'N': pagenumbers=0; break;
				case 'n': write_history=0; break;
				case 'p': pretty=0; break;
				case 'O':
					choose_outname=0;
					strcpy(outf, OUTPUTFILE);
					break;
				case 'x': include_xrefs=0; break;
				case '1': one_per_page=0; break;
				default:
					printf("++++ Cannot switch off flag: +%c\n", av[i][m]);
					return 1;
				}
				m++;
			}
			continue;
		}

		if (av[i][0]=='-') {	     /* interpret a flag with '-'*/
			m=1;
			k=strlen(av[i]);
			while (m<k) {
				switch (c = av[i][m]) {
				case 'T':			/* String Tablature */
					/* The "T" flag can be used up to 4 times, for four  */
					/* separate sets of tablature.  The first "T" switch */
					/* on the command line is for the bottom set of tab  */
					/* and so on.  String tablature is printed below all */
					/* staves but above whistle tablature.               */
					/* Get the tablature voice. */
					if (nStringTab >= MAXSTRINGTAB) {
						printf("++++ Too many string tablatures specified.\n");
						return 1;
					}
					m++;
					tab_voice = 0;
					c = av[i][m];
					if ((c >= '1') && (c <= '9')) {
						tab_voice = c - 48;
					} else {
						printf("++++ Invalid string tablature voice.\n");
						return 1;
					}
					m++;
					c = av[i][m];
					if ((c >= '0') && (c <= '9')) {
						tab_voice = (tab_voice * 10) + (c - 48);
						m++;
						c = av[i][m];
					}
					StringTab_tb[nStringTab].voice = tab_voice - 1;
					StringTab_tb[nStringTab].style = TABSIMPLE;
					StringTab_tb[nStringTab].nstrings = 0;
					StringTab_tb[nStringTab].capo = 0;
					/* Get the tablature fretting. */
					if (toupper(c) == 'C') {
						StringTab_tb[nStringTab].fretting = TABCHROMATIC;
					} else if (toupper(c) == 'D') {
						StringTab_tb[nStringTab].fretting = TABDULCIMER;
						m++;
						c = av[i][m];
						if (c == '+') {
							StringTab_tb[nStringTab].fretting = TABDULCIMERPLUS;
						} else {
							m--;
						}
					} else {
						printf("++++ Invalid string tablature fretting.\n");
						return 1;
					}
					/* Get the tuning */
					do {
						m++;
						if (m == k) break;
						c = av[i][m];
						switch (c) {
						case 'C': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 24; 
							/* There will be one and only one note-value character for a given pitch so */
							/* we use that as a trigger to increment the number of strings.             */
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'D': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 26; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'E': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 28; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'F': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 29; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'G': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 31; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'A': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 33;
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'B': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 35; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'c': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 36; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'd': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 38; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'e': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 40; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'f': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 41; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'g': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 43; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'a': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 45; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case 'b': 
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 47; 
							StringTab_tb[nStringTab].nstrings++;
							if (StringTab_tb[nStringTab].nstrings > 8) {
								printf("++++ Too many strings for tablature.\n");
								return 1;
							}
							break;
						case '\'':
							/* this character comes after a note-value so we increment */
							/* the previous pitch                                      */
							if (!StringTab_tb[nStringTab].nstrings) {
								printf("++++ Syntax error in string tablature switch.\n");
								return 1;
							}
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings - 1] += 12;
							break;
						case 'h':
						case 'H': 
							/* this character comes before a note-value so we increment*/
							/* the present pitch                                       */
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 12;
							break;
						case ',':
							/* this character comes after a note-value so we increment */
							/* the previous pitch                                      */
							if (!StringTab_tb[nStringTab].nstrings) {
								printf("++++ Syntax error in string tablature switch.\n");
								return 1;
							}
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings - 1] -= 12;
							break;
						case 'l':
						case 'L': 
							/* this character comes before a note-value so we increment*/
							/* the present pitch                                       */
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] -= 12;
							break;
						case '_':
							/* this character comes before a note-value so we increment*/
							/* the present pitch                                       */
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings]--;
							break;
						case '^':
							/* this character comes before a note-value so we increment*/
							/* the present pitch                                       */
							StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings]++;
							break;
						case 9:
						case ' ': break;	/* spaces allowed, avoid default handler */
						case ':': break;
						default:
							if ((c < '0') || (c > '9')) {
								printf("++++ invalid character for tablature.\n");
								return 1;
							}
						}
					} while ((c != ':') && !((c >= '0') && (c <= '9')));
					if ((m < k) && (c != ':')) {
						/* We have a capo number.                        */
						StringTab_tb[nStringTab].capo = c - 48;
						m++;
						c = av[i][m];
						if ((m < k) && (c >= '0') && (c <= '9')) {
							StringTab_tb[nStringTab].capo *= 10;
							StringTab_tb[nStringTab].capo += (c - 48);
							m++;
						}
					}
					c = av[i][m];
					if ((m < (k - 1)) && (c == ':')) {
						m++;
						if (!stricmp(&av[i][m], "marked")) {
							StringTab_tb[nStringTab].style = TABMARKED;
						} else {
							StringTab_tb[nStringTab].style = TABSIMPLE;
						}
					}
					nStringTab++;
					m = k;
					break;
				case 'W':			/* Whistle tablature */
					/* The "W" flag can be used up to 4 times.  The first*/
					/* time it is encountered the information applies to */
					/* the first (lowest on page) tablature line and the */
					/* second applies to the second tablature line.      */
					/* The W flag uses two parameters, the voice and the */
					/* native key of the whistle.                        */
					m++;
					w_voice = 0;
					whistle_pitch = 0;
					while (m < k) {
						c = av[i][m];
						if ((c >= '0') && (c <= '9')) {
							/* the voice selection */
							if (w_voice) {
								/* second digit of voice */
								w_voice = (w_voice * 10) + (c - 48);
							} else {
								/* first digit of voice */
								w_voice = c - 48;
							}
						} else {
							/* part of the whistle key selection */
							switch(c) {
							case 'A': whistle_pitch += 21; break;
							case 'B': whistle_pitch += 23; break;
							case 'C': whistle_pitch += 24; break;
							case 'D': whistle_pitch += 26; break;
							case 'E': whistle_pitch += 28; break;
							case 'F': whistle_pitch += 29; break;
							case 'G': whistle_pitch += 31; break;
							case 'a': whistle_pitch += 33; break;
							case 'b':
								if (whistle_pitch) {
									/* Assume this is a "flat" character */
									whistle_pitch--;
								} else {
									/* we haven't specified a note letter yet so this must be "B" */
									whistle_pitch += 35;
								}
								break;
							case 'c': whistle_pitch += 36; break;
							case 'd': whistle_pitch += 38; break;
							case 'e': whistle_pitch += 40; break;
							case 'f': whistle_pitch += 41; break;
							case 'g': whistle_pitch += 43; break;
							case '\'':
							case 'h':
							case 'H': whistle_pitch += 12; break;
							case ',':
							case 'l':
							case 'L': whistle_pitch -= 12; break;
							case '_': whistle_pitch--; break;
							case '^':
							case '#': whistle_pitch++; break;
							case 9:
							case ' ': break;	/* spaces allowed, avoid default handler */
							default:
								printf("++++ Invalid character <%c> for whistle key.\n", c);
								return 1;
							}
						}
						m++;
					}
					if (nWhistleTab < MAXWHISTLETAB) {
						WhistleTab_tb[nWhistleTab].voice = w_voice - 1;
						WhistleTab_tb[nWhistleTab].keypitch = whistle_pitch;
						nWhistleTab++;
					} else {
						printf("++++ Too many whistle tablature selections.\n");
						return 1;
					}
					break;
					/* simple flags */
				case 'A': select_all=1; break;
				case 'b': break_continues=1; continue_lines=0; break;
				case 'C': s_field[nsel]=S_COMPOSER; sel_arg=1; break;
				case 'c': continue_lines=1; break_continues=0; break;
				case 'E': epsf=1; break;
				case 'e': sel_arg=1; break;
				case 'f':
					nsel++;
					strcpy(sel_str[nsel], "");
					if (ninf==0) {		/* selector before first file */
						strcpy(sel_str[nsel],sel_str[nsel-1]);
						s_field[nsel]=s_field[nsel-1];
					}
					s_field[nsel]=search_field0;
					sel_arg=0;
					f_pos=i;
					break;
				case 'h': help_me=1; break;
				case 'i': interactive=1; break;
				case 'l': landscape=1; break;
				case 'L': FixEols = TRUE; break;
				case 'M': music_only=1; break;
				case 'n': write_history=1; break;
				case 'P': pretty=2; break;
				case 'p': pretty=1; break;
				case 'o': do_mode=DO_OUTPUT; break;
				case 'N': pagenumbers=1;
				case 'R': s_field[nsel]=S_RHYTHM; sel_arg=1; break;
				case 'S': s_field[nsel]=S_SOURCE; sel_arg=1; break;
				case 'x': include_xrefs=1; break;
				case '1': one_per_page=1; break;
				/* flags with parameter.. */
				case 'H': 
				case 'a':	
				case 'B':
				case 'D':
				case 'd':
				case 'F':
				case 'g':
				case 'j':
				case 'm':
				case 'O':
				case 's':
				case 'v':
				case 'V':
				case 'w':
				case 'Y':
				case '#':
					strcpy(aaa, &av[i][m+1]);
					if (aaa[0] != '\0'
					    && strchr("glO",c)) {	/* no sticky arg */
						printf("++++ Incorrect usage of flag -%c\n", c);
						return 1;
					}

					got_value=1;			/* check for value */
					if (aaa[0] == '\0') {
						i++;
						if ((i>=ac) || (av[i][0]=='-'))
							got_value=0;
						else	strcpy(aaa,av[i]);
					}

					if (got_value
					    && strchr("vsYBj#",c)) {	    /* check num args */
						ok=1;
						for (j=0;j<(int)strlen(aaa);j++)
							if (!strchr("0123456789.",aaa[j]))
								ok=0;
						if (!ok) {
							printf("++++ Invalid parameter <%s> for flag -%c\n",aaa,c);
							return 1;
						}
					}

					if (!got_value && (c != 'H')) {	/* check value was given */
						printf("++++ Missing parameter after flag -%c\n",
						       c);
						return 1;
					}
					switch (c) {
					case '#':			/* set page number */
						sscanf (aaa, "%d", &FirstPgNum);
						FirstPgNum--;
						break;
					case 'H':
						help_me=2;
						/* Value is optional. */
						if (!stricmp(aaa, "cm")) {
							ParamUnits = 1;
						} else if (!stricmp(aaa, "pt")) {
							ParamUnits = 2;
						} else {
							ParamUnits = 0;
						}
						break;
					case 'O': 			/* -O flag  */
						if (!strcmp(aaa,"=")) {
							choose_outname=1;
						} else {
							getext(aaa, ext);
							if (strcmp(ext,"ps")
							    && strcmp(ext,"eps")
							    && strcmp(ext,"")) {
								printf("Wrong extension for output file: %s\n",
								       aaa);
								return 1;
							}
							strcpy(outf, aaa);
							strext(outf, "ps");
							choose_outname=0;
						}
						break;
					case 'a':
						sscanf (aaa, "%f", &alfa_c);
						if ((alfa_c>1.05)||(alfa_c<-0.01)) {
							printf("++++ Bad parameter for flag -a: %s\n",
							       aaa);
							return 1;
						}
						break;
					case 'B':
						sscanf(aaa,"%d",&bars_per_line);
						continue_lines=0;
						break;
					case 'j':
						sscanf(aaa,"%d",&measure_nb);
						break;
					case 'v':
						sscanf(aaa,"%d",&vb);
						break;
					case 'V':
						sscanf(aaa, "%d", &w_voice);
						if ((w_voice > 0) && (w_voice <= MAXVOICE)) VoiceMute_tb[w_voice-1] = TRUE;
						break;
					case 's':
						sscanf(aaa,"%f",&scalefac);
						break;
					case 'd':
						staffsep = scan_u(aaa);
						break;
					case 'w':
						swidth = scan_u(aaa);
						break;
					case 'm':
						lmargin = scan_u(aaa);
						break;
					case 'F':
						strcpy(styf,aaa);
						break;
					case 'D':
						strcpy(styd,aaa);
						break;
					case 'g':
						if    (abbrev(aaa,"shrink", 2)) gmode=G_SHRINK;
						else if (abbrev(aaa,"stretch",2)) gmode=G_STRETCH;
						else if (abbrev(aaa,"space",  2)) gmode=G_SPACE;
						else if (abbrev(aaa,"fill",   2)) gmode=G_FILL;
						else {
							printf("++++ Bad parameter for flag -g: %s\n",
							       aaa);
							return 1;
						}
						break;
					}
					m = k;		/* stop */
					break;
				default:
					printf("++++ Unknown flag: -%c\n", c);
					return 1;
				}
				m++;
			}
			continue;
		}

		if (strstr(av[i],".fmt"))	/* implict -F */
			strcpy(styf, av[i]);
		else {
			if (strstr(av[i],".abc") && sel_arg) {	   /* file if .abc */
				nsel++;
				strcpy(sel_str[nsel], "");
				s_field[nsel]=S_TITLE;
				if (ninf==0) {		/* selector before first file */
					strcpy(sel_str[nsel],sel_str[nsel-1]);
					s_field[nsel]=s_field[nsel-1];
				}
				sel_arg=0;
			}
			if (is_xrefstr(av[i]) && (!sel_arg)) {	  /* sel if xref numbers */
				if (i-1 != f_pos)
					sel_arg=1;
			}

			if (!sel_arg) { 		/* this arg is a file name */
				if (ninf>=MAXINF) {
					printf("++++ Too many input files, max is %d\n", MAXINF);
					return 1;
				}
				strcpy(in_file[ninf], av[i]);
				psel[ninf]=nsel;
				ninf++;
			} else {			/* this arg is a selector */
				strcat(sel_str[nsel], av[i]);
				strcat(sel_str[nsel], " ");
			}
		}
	}
	return 0;
}

/* ----- set_page_format ----- */
int set_page_format()
{
	int i,j;

	if (pretty==1)
		set_pretty_format(&cfmt);
	else if (pretty==2)
		set_pretty2_format(&cfmt);
	else	set_standard_format(&cfmt);

	i=read_fmt_file("fonts.fmt", styd, &cfmt);
	j=0;
	if (styf[0] != '\0') {
		strext(styf, "fmt");
		j=read_fmt_file(styf, styd, &cfmt);
		if (j==0) {
			printf("\n++++ Cannot open file: %s\n", styf);
			return 0;
		}
		strcpy(cfmt.name, styf);
	}
	if (i || j)
		printf("\n");
	ops_into_fmt(&cfmt);

	make_font_list(&cfmt);
	sfmt=cfmt;
	dfmt=cfmt;
	return 1;
}

/* ----- tex_str: change string to take care of some tex-style codes --- */
/* Puts \ in front of ( and ) in case brackets are not balanced,
   interprets some TeX-type strings using ISOLatin1 encodings.
   Returns the length of the string as finally given out on paper.
   Also returns an estimate of the string width... */
int tex_str(char *c,
	    char s[],
	    float *wid)
{
	int base,add,n;
	char t[21];
	float w;

	s[0] = '\0';
	n=0;
	w=0;
	while (*c != '\0') {
		switch (*c) {
		case '(':
		case ')':	 	  /* ( ) becomes \( \) */
			sprintf(t, "\\%c", *c); strcat(s,t); w+=cwid('('); n++;
			break;
		case '\\':		  /* backslash sequences */
			c++;
			if (*c=='\0')
				break;
			add=0;				  /* accented vowels */
			switch (*c) {
			case '`': add=1; break;
			case '\'': add=2; break;
			case '^': add=3; break;
			case '"': add=4; break;
			}
			if (add) {
				c++;
				base=0;
				switch (*c) {
				case 'a': base=340; if (add==4) add=5; break;
				case 'e': base=350; break;
				case 'i': base=354;
				case 'o': base=362; if (add==4) add=5; break;
				case 'u': base=371; break;
				case 'A': base=300; if (add==4) add=5; break;
				case 'E': base=310; break;
				case 'I': base=314; break;
				case 'O': base=322; if (add==4) add=5; break;
				case 'U': base=331; break;
				}
				w+=cwid(*c);
				if (base)
					sprintf(t,"\\%d",base+add-1);
				else	sprintf(t,"%c",*c);
				strcat(s,t);
				n++;
			}

			else if (*c==' ') 			      /* \-space */
				{ strcat(s," "); w+=cwid(' '); n++; }

			else if (*c=='O') 			      /* O-slash */
				{ strcat(s,"\\330"); w+=cwid('O'); n++; }

			else if (*c=='o') 			      /* o-slash */
				{ strcat(s,"\\370"); w+=cwid('O'); n++; }

			else if((*c=='s')&&(*(c+1)=='s')) 	      /* sz */
				{ c++; strcat(s,"\\337"); w+=cwid('s'); n++; } 
			else if((*c=='a')&&(*(c+1)=='a')) 	      /* a-ring */
				{ c++; strcat(s,"\\345"); w+=cwid('a'); n++; }
			else if((*c=='A')&&(*(c+1)=='A')) 	      /* A-ring */
				{ c++; strcat(s,"\\305"); w+=cwid('A'); n++; }
			else if((*c=='a')&&(*(c+1)=='e')) 	      /* ae */
				{ c++; strcat(s,"\\346"); w+=(float)1.5*cwid('a'); n++; }
			else if((*c=='A')&&(*(c+1)=='E')) 	      /* AE */
				{ c++; strcat(s,"\\306"); w+=(float)1.5*cwid('A'); n++; }

			else if (*c=='c') {			    /* c-cedilla */
				c++;
				w+=cwid(*c);
				if	(*c=='C') { strcat(s,"\\307"); n++; }
				else if (*c=='c') { strcat(s,"\\347"); n++; }
				else		  {sprintf(t,"%c",*c); strcat(s,t); n++; }
			}

			else if (*c=='~') {			    /* n-twiddle */
				c++;
				w+=cwid(*c);
				if	(*c=='N') { strcat(s,"\\321"); n++; }
				else if (*c=='n') { strcat(s,"\\361"); n++; }
				else		  { sprintf(t,"%c",*c); strcat(s,t); n++; }
			}

			else			/* \-something-else; pass through */
				{sprintf(t,"\\%c",*c); strcat(s,t); w+=cwid('A'); n++; }
			break;

		case '{':
		case '}':
			break;

		default:		/* other characters: pass though */
			sprintf(t,"%c",*c);
			strcat(s,t); n++;
			w+=cwid(*c);
		}
		c++;
	}
	*wid = w;

	return n;
}

/* ----- put_str: output a string in postscript ----- */
static void put_str(char *str)
{
	char s[801];
	float w;

	tex_str(str, s, &w);
	PUT1("%s", s);
}

/* ----- set_font ----- */
void set_font(struct FONTSPEC font,
	      int add_bracket)
{
	int fnum;

	for (fnum = nfontnames; fnum >= 0; fnum--) {
		if (!strcmp(font.name,fontnames[fnum]))
			break;
	}
	if (fnum<0) {
		printf("\n++++ Font \"%s\" not predefined; using first in list\n",
			font.name);
		fnum=0;
	}
	PUT2(" %.1f F%d ", font.size, fnum);
	if (add_bracket)
		PUT0("(");
}

/* ----- set_font_str ----- */
static void set_font_str(char str[],
			 struct FONTSPEC font)
{
	int fnum;

	for (fnum = nfontnames; fnum >= 0; fnum--) {
		if (!strcmp(font.name,fontnames[fnum]))
			break;
	}
	sprintf(str,"%.1f F%d ", font.size, fnum);
}

/* ----- check_margin: do horizontal shift if needed ---- */
void check_margin(float new_posx)
{
	float dif;

	dif=new_posx-posx;
	if (dif*dif<0.001)
		return;

	PUT1("%.2f 0 T\n", dif);
	posx=new_posx;
}

/* ----- epsf_title ------ */
void epsf_title(char title[],
		char fnm[])
{
	char *p,*q;

	p=title; q=fnm;
	while (*p != '\0') {
		if (*p == ' ')
			*q++ = '_';
		else	*q++ = *p;
		p++;
	}
	*q = '\0';
}

/* ----- close_output_file ------ */
void close_output_file()
{
	int m;

	if (!file_open)
		return;

	if (interactive)
		printf("(close %s)\n", outfnam);

	close_page(fout);
	close_ps(fout);
	fclose(fout);
	if (tunenum==0)
		printf("++++ Warning: no tunes written to output file\n");
	m=get_file_size (outfnam);
	printf("Output written on %s (%d page%s, %d title%s, %d byte%s)\n",
		outfnam,
		pagenum, pagenum==1 ? "" : "s",
		tunenum, tunenum==1 ? "" : "s",
		m, m==1 ? "" : "s");
	file_open=0;
	file_initialized=0;
}

/* ----- open_output_file ------ */
void open_output_file(char fnam[],
		      char tstr[])
{
	if (!strcmp(fnam,outfnam))
		return;

	if (file_open)
		close_output_file();

	if (interactive)
		printf("(open %s)\n", fnam);
	strcpy(outfnam, fnam);
	if ((fout = fopen(outfnam,"w")) == NULL)
		rx("Cannot open output file ", outf);
	tunenum=tnum1=tnum2=0;
	pagenum=0;
	file_open=1;
	file_initialized=0;
}

/* ----- add_to_text_block ----- */
void add_to_text_block(char ln[],
		       int add_final_nl)
{
	char *c,*a;
	char word[81];
	int nt,nl;

	nt=ntxt;
	c=ln;

	for (;;) {
		while (*c==' ')
			c++;
		if (*c=='\0')
			break;
		a=word;
		nl=0;
		while ((*c!=' ')&&(*c!='\0')&&(*c!='\n')) {
			if ((*c=='\\')&&(*(c+1)=='\\')) {
				nl=1;
				c+=2;
				break;
			}
			*a++=*c++;
		}
		*a='\0';
		if (strlen(word)>MAXWLEN) {
			word[MAXWLEN-1]='\0';
			printf("++++ Insanely long word truncated to %d chars: %s\n", 
				MAXWLEN-1,word);
		}
		if (nt>=MAXNTEXT) {
			printf("\n++++ Line %d: %s\n", linenum,ln);
			rx("Text overflow; increase MAXNTEXT and recompile.","");
		}
		if (word[0] != '\0') {
			strcpy(txt[nt],word);
			nt++;
		}
		if (nl) {
			strcpy(txt[nt],"$$NL$$");
			nt++;
		}
	}
	if (add_final_nl) {
		strcpy(txt[nt],"$$NL$$");
		nt++;
	}
	ntxt=nt;
}

/* ----- write_text_block ----- */
void write_text_block(FILE *fp,
		      int  job)
{
	int i,i1,i2,ntline,nc,mc,nbreak;
	float textwidth,ftline,ftline0,swfac,baseskip,parskip;
	float wwidth,wtot,spw;
	char str[81];

	if (ntxt<=0)
		return;

	baseskip = cfmt.textfont.size * cfmt.lineskipfac;
	parskip = cfmt.textfont.size * cfmt.parskipfac;
	set_font_str(page_init,cfmt.textfont);

	/* estimate text widths.. ok for T-R, wild guess for other fonts */
	swfac=(float)1.0;
	if (strstr(cfmt.textfont.name,"Times-Roman"))
		swfac=(float)1.00;
	else if (strstr(cfmt.textfont.name,"Times-Bold"))
		swfac=(float)1.05;
	else if (strstr(cfmt.textfont.name,"Helvetica"))
		swfac=(float)1.10;
	else if (strstr(cfmt.textfont.name,"Helvetica-Bold"))
		swfac=(float)1.15;
	else if (strstr(cfmt.textfont.name,"Palatino"))
		swfac=(float)1.10;
/*	swfac=1.0; ??*/
	spw=cwid(' ');
	PUT1("/LF {0 %.1f rmoveto} bind def\n",-baseskip);

	/* output by pieces, separate at newline token */
	i1=0;
	while (i1<ntxt) {
		i2=-1;
		for (i=i1;i<ntxt;i++)
			if(!strcmp(txt[i],"$$NL$$")) {
				i2=i;
				break;
			}
		if (i2==-1)
			i2=ntxt;
		bskip(baseskip);

		if (job==OBEYLINES) {
			PUT0("0 0 M (");
			for (i=i1;i<i2;i++) {
				tex_str(txt[i],str,&wwidth);
				PUT1("%s ",str);
			}
			PUT0(") show\n");
		} else if (job==OBEYCENTER) {
			PUT1("%.1f 0 M (",cfmt.staffwidth/2);
			for (i=i1;i<i2;i++) {
				tex_str(txt[i],str,&wwidth);
				PUT1("%s",str);
				if (i<i2-1)
					PUT0(" ");
			}
			PUT0(") cshow\n");
		} else {
			PUT0("0 0 M mark\n");
			nc=0;
			mc=-1;
			wtot=-spw;
			for (i=i2-1;i>=i1;i--) {
				mc+=tex_str(txt[i],str,&wwidth)+1;
				wtot+=wwidth+spw;
				nc+=strlen(str)+2;
				if (nc>=72) {
					nc=0; PUT0("\n");
				}
				PUT1 ("(%s)",str);
			}
			if (job==RAGGED)
				PUT1(" %.1f P1\n",cfmt.staffwidth);
			else	PUT1(" %.1f P2\n",cfmt.staffwidth);
			/* first estimate: (total textwidth)/(available width) */
			textwidth=wtot*swfac*cfmt.textfont.size;
			if (strstr(cfmt.textfont.name,"Courier"))
				textwidth=(float)0.60*mc*cfmt.textfont.size;
			ftline0=textwidth/cfmt.staffwidth;
			/* revised estimate: assume some chars lost at each line end */
			nbreak=(int)ftline0;
			textwidth=textwidth+5*nbreak*cwid('a')*swfac*cfmt.textfont.size;
			ftline=textwidth/cfmt.staffwidth;
			ntline=(int)ftline+1;
			if (vb>=10) {
				printf("first estimate %.2f, revised %.2f\n", ftline0,ftline);
				printf("Output %d word%s, about %.2f lines (fac %.2f)\n",
				       i2-i1, i2-i1==1?"":"s", ftline,swfac);
			}
			bskip((ntline-1)*baseskip);
		}

		buffer_eob(fp);
		/* next line to allow pagebreak after each text "line" */
		/* if (!epsf && !within_tune) write_buffer(fp); */
		i1=i2+1;
	}
	bskip(parskip);
	buffer_eob(fp);
	/* next line to allow pagebreak after each paragraph */
	if (!epsf && !within_tune)
		write_buffer(fp);
	page_init[0] = '\0';
}

/* ----- put_words ------- */
void put_words(FILE *fp)
{
	int i,nw,n;
	char str[81];
	char *p,*q;

	set_font(cfmt.wordsfont, 0);
	set_font_str(page_init,cfmt.wordsfont);

	n=0;
	for (i=0;i<ntext;i++)
		if (text_type[i]==TEXT_W)
			n++;
	if (n==0)
		return;

	bskip(cfmt.wordsspace);
	for (i=0;i<ntext;i++) {
		if (text_type[i]==TEXT_W) {
			bskip(cfmt.lineskipfac*cfmt.wordsfont.size);
			p=&text[i][0];
			q=&str[0];
			if (isdigit(text[i][0])) {
				while (*p != '\0') {
					*q++ = *p++;
					if (*p==' '
					    || *(p-1)==':'
					    || *(p-1)=='.')
						break;
				}
				if (*p==' ')
					p++;
			}
			*q='\0';

			/* permit page break at empty lines or stanza start */
			nw=nwords(text[i]);
			if ((nw==0) || str[0] != '\0')
				buffer_eob(fp);

			if (nw>0) {
				if (str[0] != '\0') {
					PUT0("45 0 M (");
					put_str(str);
					PUT0(") lshow\n");
				}
				if (*p != '\0') {
					PUT0("50 0 M (");
					put_str(p);
					PUT0(") show\n");
				}
			}
		}
	}

	buffer_eob(fp);
	page_init[0] = '\0';
}

/* ----- put_text ------- */
void put_text(FILE *fp,
	      int type,
	      char str[])
{
	int i,n;
	float baseskip,parskip;

	n=0;
	for (i=0;i<ntext;i++)
		if (text_type[i]==type)
			n++;
	if (n==0)
		return;

	baseskip = cfmt.textfont.size * cfmt.lineskipfac;
	parskip  = cfmt.textfont.size * cfmt.parskipfac;
	PUT0("0 0 M\n");
	ntxt=0;
	add_to_text_block(str,0);
	for (i=0;i<ntext;i++) {
		if (text_type[i]==type)
			add_to_text_block(text[i],1);
	}
	write_text_block(fp,RAGGED);
	buffer_eob(fp);
}

/* ----- PutSource [JSA] -- */
void PutSource(FILE *fp) {
	if (!info.src[0]) return;
	set_font(cfmt.SourceFont, FALSE);
	PUT1("%.1f 0 M (", cfmt.staffwidth / (float)2.0);
	put_str(info.src);
	PUT0(") cshow\n");
	bskip(cfmt.SourceFont.size);
}

/* ----- put_history ------- */
void put_history(FILE *fp)
{
	int i,ok;
	float baseskip,parskip;

	set_font(cfmt.textfont,0);
	set_font_str(page_init,cfmt.textfont);
	baseskip = cfmt.textfont.size * cfmt.lineskipfac;
	parskip  = cfmt.textfont.size * cfmt.parskipfac;

	bskip(cfmt.textspace);

	if ((info.rhyth[0] != '\0') && cfmt.ShowRhythm) {
		bskip(baseskip);
		PUT0("0 0 M (Rhythm: ");
		put_str (info.rhyth);
		PUT0(") show\n");
		bskip(parskip);
	}

	/* TODO: This whole function needs attention. */
	/* Immediate problem, "book" is printing the title of each tune. */
	/*
	if (info.book[0] != '\0') {
		bskip((float)0.5*CM);
		PUT0("0 0 M (Book: ");
		put_str(info.book);
		PUT0(") show\n");
		bskip(parskip);
	}
	*/

	/* replaced by PutSource [JSA] */
	/*
	if (info.src[0] != '\0') {
		bskip((float)0.5*CM);
		PUT0("0 0 M (Source: ");
		put_str(info.src);
		PUT0(") show\n");
		bskip(parskip);
	}
	*/

	put_text(fp, TEXT_D, "Discography: ");
	put_text(fp, TEXT_N, "Notes: ");
	put_text(fp, TEXT_Z, "Transcription: ");


	ok=0;
	set_font(cfmt.HistoryFont, FALSE);
	for (i=0;i<ntext;i++) {
		if (text_type[i]==TEXT_H) {
			bskip(cfmt.HistoryFont.size * cfmt.lineskipfac);
			PUT0("0 0 M (");
			put_str(text[i]);
			PUT0(") show\n");
			ok=1;
		}
	}
	if (ok)	bskip(parskip);
	buffer_eob(fp);
	strcpy(page_init,"");
}

/* ----- write_inside_title  ----- */
void write_inside_title(void)
{
	char t[201];

	bskip(cfmt.subtitlefont.size+(float)0.2*CM);
	set_font(cfmt.subtitlefont, 0);

	if (numtitle==1)
		strcpy(t,info.title);
	else if (numtitle==2)
		strcpy(t,info.title2);
	else if (numtitle==3)
		strcpy(t,info.title3);
	if (vb>15)
		printf("write inside title <%s>\n", t);

	if (cfmt.titlecaps)
		cap_str(t);
	PUT0(" (");
	put_str(t);
	if (cfmt.titleleft)
		PUT0(") 0 0 M show\n");
	else	PUT1(") %.1f 0 M cshow\n", cfmt.staffwidth/2);
	bskip(cfmt.musicspace+(float)0.2*CM);
}

/* ----- write_tunetop ----- */
void write_tunetop(void)
{
	PUT2("\n\n%% --- tune %d %s\n", tunenum+1, info.title);
	if (!epsf)
		bskip(cfmt.topspace);
}

/* ----- write_tempo ----- */
static void write_tempo(char tempo[])
{
	char *r, *q;
	char text[STRL];
	int top,bot,value,len,i;
	struct SYMBOL s;
	float stem,dotx,doty,sc,dx;

	if (!cfmt.ShowTempo) return;

	p0 = tempo;	/* (for syntax error) */
	if (vb>15)
		printf("write tempo <%s>\n", info.tempo);
	r=tempo;
	q=text;
	set_font(cfmt.tempofont,0);

	/* Skip leading spaces, changed to skip all whitespace. */
	while (isspace(*r)) r++;
	if (*r == '"') {
		r++;
		while ((*r != '"') && (*r != '\0')) *q++ = *r++;
		if (*r == '"') r++;
	}
	*q = '\0';
	while (isspace(*r)) r++;

	PUT0(" 25 0 M ");

	/* draw the string, if specified */
	if (text[0] != '\0') {
		PUT0(" (");
		put_str(text);
		PUT0("   ) show\n");
	}

	/* draw the tempo indication, if specified */
	if (*r != '\0') {
		if (*r=='C' || *r=='c') {
			p=r+1;
			len=parse_length();
			if (*p!='=')
				printf("\n++++ invalid tempo specifier: %s\n", r);
			sscanf(p,"=%d", &value);
		} else if (isdigit(*r)) {
			if (sscanf(r,"%d/%d=%d", &top,&bot,&value) == 3)
				len=(BASE*top)/bot;
			else	{
				value = atoi(r);
				p = &tempo[strlen(tempo)];	/* empty string */
				len = parse_length();		/* get default length */
			}
		} else	{
			printf("\n++++ invalid tempo specifier: %s\n", r);
			len = EIGHTH;
		}

		s.len=len;
		identify_note(&s,r);
		/* draw the note */
		sc=(float)0.55*cfmt.tempofont.size/(float)10.0;
		PUT2("gsave %.2f %.2f scale 15 3 rmoveto currentpoint\n", sc,sc);
		if (s.head==H_OVAL)
			PUT0("HD");
		else if (s.head==H_EMPTY)
			PUT0("Hd");
		else if (s.head==H_FULL)
			PUT0("hd");
		dx=4.0;
		if (s.dots) {
			dotx=8; doty=0;
			if (s.flags>0)
				dotx += 4;
			if (s.head==H_EMPTY)
				dotx += 1;
			else if (s.head==H_OVAL)
				dotx += 2;
			for (i=0;i<s.dots;i++) {
				PUT2(" %.1f %.1f dt", dotx, doty);
				dx=dotx;
				dotx += 3.5;
			}
		}
		stem=16.0;
		if (s.len<WHOLE)
			PUT1(" %.1f su",stem);
		if (s.flags>0)
			PUT2(" %.1f f%du",stem,s.flags);
		if ((s.flags>0) && (dx<6.0))
			dx=6.0;
		dx=(dx+18)*sc;
		PUT2(" grestore %.2f 0 rmoveto ( = %d) show\n", dx,value);
	}
}

/* ----- write_inside_tempo  ----- */
void write_inside_tempo(void)
{
	bskip(cfmt.partsfont.size);
	write_tempo(info.tempo);
	bskip((float)0.1*CM);
}

/* ----- write_heading	----- */
void write_heading(void)
{
	float lwidth,down1,down2;
	int i,ncl;
	char t[201], tbuff[256], *tptr;

	lwidth=cfmt.staffwidth;

	/* write the main title */
	bskip(cfmt.titlefont.size+cfmt.titlespace);
	set_font(cfmt.titlefont,1);
	if (cfmt.withxrefs)
		PUT1("%d. ", xrefnum);
	strcpy(t,info.title);
	if (cfmt.titlecaps)
		cap_str(t);
	put_str(t);
	if (cfmt.titleleft)
		PUT0(") 0 0 M show\n");
	else	PUT1(") %.1f 0 M cshow\n", lwidth/2);

	/* write second title */
	if (numtitle>=2) {
		bskip(cfmt.subtitlespace+cfmt.subtitlefont.size);
		set_font(cfmt.subtitlefont,1);
		strcpy(t,info.title2);
		if (cfmt.titlecaps)
			cap_str(t);
		put_str(t);
		if (cfmt.titleleft)
			PUT0(") 0 0 M show\n");
		else	PUT1(") %.1f 0 M cshow\n", lwidth/2);
	}

	/* write third title */
	if (numtitle>=3) {
		bskip(cfmt.subtitlespace+cfmt.subtitlefont.size);
		set_font(cfmt.subtitlefont,1);
		strcpy(t,info.title3);
		if (cfmt.titlecaps)
			cap_str(t);
		put_str(t);
		if (cfmt.titleleft)
			PUT0(") 0 0 M show\n");
		else	PUT1(") %.1f 0 M cshow\n", lwidth/2);
	}

	/* write composer, origin */
	/* Changed to split composer to left/right side of page when it */
	/* contains a forward slash "/" and drops the origin to the     */
	/* next line (if present).  If there is not either an embedded  */
	/* slash or exactly two composer entries, the behaviour remains */
	/* unchanged from abcm2ps. [JSA]                                */
	set_font(cfmt.composerfont,0);
	bskip(cfmt.composerspace);
	down1=cfmt.composerspace + cfmt.musicspace + cfmt.composerfont.size;
	if (info.ncomp == 2) {
		// Put the first comp on the left and the second on the right.
		bskip(cfmt.composerfont.size);
		PUT0("0 0 M (");
		put_str(info.comp[0]);
		PUT0(") show\n");
		PUT1("%.1f 0 M (", lwidth);
		put_str(info.comp[1]);
		PUT0(") lshow\n");
	} else if ((info.ncomp == 1) && (strchr(info.comp[0], '/'))) {
		// Divide the string into left and right sections at the '/'
		strncpy(tbuff, info.comp[0], 255);
		tptr = strchr(tbuff, '/');
		if (tptr) *tptr = 0;
		bskip(cfmt.composerfont.size);
		PUT0("0 0 M (");
		put_str(tbuff);
		PUT0(") show\n");
		if (tptr) {
			tptr++;
			if (*tptr) {
				PUT1("%.1f 0 M (", lwidth);
				put_str(tptr);
				PUT0(") lshow\n");
			}
		}
	} else if ((info.ncomp>0) || info.orig[0] != '\0') {
		ncl=info.ncomp;
		if (info.orig[0] != '\0' && ncl < 1) ncl=1;
		for (i=0;i<ncl;i++) {
			bskip(cfmt.composerfont.size);
			PUT1("%.1f 0 M (", lwidth);
			if (info.comp[i] != 0)
				put_str(info.comp[i]);
			if ((i==ncl-1)&& info.orig[0] != '\0') {
				put_str(" (");
				put_str(info.orig);
				put_str(")");
			}
			PUT0(") lshow\n");
		}
		down1 += (ncl - 1) * cfmt.composerfont.size;
	} else {
		bskip(cfmt.composerfont.size);
	}
	bskip(cfmt.musicspace);

	/* decide whether we need extra shift for parts and tempo */
	down2=cfmt.composerspace+cfmt.musicspace;
	if (info.parts[0] != '\0')
		down2 += cfmt.partsspace+cfmt.partsfont.size;
	if (info.tempo[0] != '\0')
		down2 += cfmt.partsspace+cfmt.partsfont.size;
	if (down2>down1)
		bskip(down2-down1);

	/* write tempo and parts */
	if (info.parts[0] != '\0' || info.tempo[0] != '\0') {
		if (info.tempo[0] != '\0') {
			bskip((float)-0.3*CM);
			write_tempo(info.tempo);
			bskip(-cfmt.tempofont.size);
		}
		if (info.parts[0] != '\0') {
			bskip(-cfmt.partsspace);
			set_font(cfmt.partsfont,0);
			PUT0("0 0 M (");
			put_str(info.parts);
			PUT0(") show\n");
			bskip(cfmt.partsspace);
		}
		if (info.tempo[0] != '\0')
			bskip(cfmt.tempofont.size+(float)0.3*CM);
	}
}

/* ----- write_parts  ----- */
void write_parts(void)
{
	if (info.parts[0] != '\0') {
		bskip(cfmt.partsfont.size);
		set_font(cfmt.partsfont,0);
		PUT0("0 0 M (");
		put_str(info.parts);
		PUT0(") show\n");
		bskip(cfmt.partsspace);
	}
}
