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

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

#include "jcabc2ps.h"
#include "style.h"
#include "buffer.h"
#include "util.h"
#include "format.h"
#include "pssubs.h"
#include "parse.h"

#include "subs.h"


/*  miscellaneous subroutines  */

/* ----- write_help ----- */
void write_help ()
{
	fprintf(stderr,"jcabc2ps version %s compiled %s\n",
					version, __DATE__);

	fprintf(stderr,"Usage: jcabc2ps [files]..  [-e nums-or-pats] [other flags] [<infile.abc] [>outfile.ps]\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"
					"       +a      write audio file %s\n"
					"       +E      produce EPSF output, one tune per file\n"
					"       +h      show this command summary (on stderr)\n"
					"       +i      run in interactive mode\n"
					"       +I      write index to Ind.ps\n"
					"       +o aa   set outfile name to aaa\n"
//					"       -q mn.. select audio quality for the voices\n"
					"       -Q xx   playback tempo, in beats per minute\n"
					"       -v nn   set verbosity level to nn\n"
					"  Selection:\n"
					"       -e      following arguments are selectors\n"
					"       -f      following arguments are file names\n"
					"       -T      search  Title   field (default)\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"
					"       -V str  select voices, eg. -V 1,4-5\n"
					"  Formatting:\n"
					"       -1      write one tune per page\n"
					"       -D bar  look for format files in directory \"bar\"\n"
					"       -d xx   set staff separation (cm/in/pt)\n"
					"       +F foo  read format from \"foo.fmt\"\n"
					"       -g xx   set glue mode to shrink|space|stretch|fill\n"
					"       +H      show the format parameters\n"
					"       -k nn   number every nn bars; 0 for first in staff\n"
					"       +l      landscape mode\n"
					"       -m xx   set left margin (cm/in/pt)\n"
					"       +n      include notes and history in output\n"
					"       +N      write page numbers\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"
					"       -X x    set strictness for note spacing, 0<x<1 \n"
					"       +x      include xref numbers in output\n"
//					"  Transposing:\n"
//					"       -t n    transpose by n halftones (_n for negative number)\n"
//					"       -t XX   transpose root up/down to note XX (eg. C A# ^Bb _Bb)\n"
					"  Line breaks:\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"
					"  Alloc options:\n"
					"     -maxs n   set maximal number of symbols (default %d)\n"
					"     -maxv n   set maximal number of voices (default %d)\n"
					, AUDIOFILE, maxSyms, maxVc);
}


/* ----- write_version ----- */
void write_version ()
{

/*  fprintf(stderr,"jcabc2ps v%s (%s, %s) compiled %s\n", VERSION, VDATE, STYLE, __DATE__); */

	fprintf(stderr,"jcabc2ps version %s compiled %s\n", version, __DATE__);

	if (strlen(DEFAULT_FDIR)>0)
		fprintf(stderr,"Default format directory %s\n", DEFAULT_FDIR);

}

/* ----- is_xrefstr: check if string ok for xref selection ---- */
int is_xrefstr (str)
	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 (str, av)
	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;
}

char V_name[128]= {0};	// Verbosity name
char V_file[128]= {0};	// Verbosity file

/* ----- init_ops ----- */
void init_ops (job)
	int job;
{	char *F = "init_ops";
	char *p;
	int   i, f;
	V4 "%s: Called with job=%d myname=\"%s\".\n",F,job,myname V;
	sprintf(V_name,"V_%s%c",myname,0);
	if ((p = getenv("V_abc2ps"))) {
		if (sscanf(p,"%d",&db) < 1)	// Original "debug" output flag
			db = DB_DF + 1;
	} elsif ((p = getenv("D_abc2ps"))) {
		if (sscanf(p,"%d",&db) < 1)	// Original "debug" output flag
			db = DB_DF + 1;
	}
	if ((p = getenv(V_name))) {
		V3 "%s: ENV(%s)=\"%s\"\n",F,V_name,p V;
		if ((i = sscanf(p,"%d%s",&vb,(char*)&V_file)) < 1) {	// JC's "verbose" flag
			vb = VB_DF + 1;
		} elsif (i > 1 && *V_file) {	// Did we get a file name?
			if (freopen(V_file,"w",stderr)) {	// Make it our stderr file.
				V4 "%s: Reopened stderr as \"%s\"\n",F,V_file V;
			} else {
				V1 "%s: Can't open stderr to \"%s\"\n",F,V_file V;
			}
		} else {
			V3 "%s: Verbose level %d to stderr.\n",F,vb V;
		}
	}
	V1 "%s: db=%d vb=%d.\n",mypath,db,vb V;
//
	one_per_page         = -1;
	landscape            = -1;
	slurgraces           = 0;
	scalefac             = -1.0;
	lmargin              = -1.0;
	swidth               = -1.0;
	write_history        = -1;
	staffsep             = -1;
	dstaffsep            =  0;
	break_continues      = -1;
	continue_lines       = -1;
	include_xrefs        = -1;
	alfa_c               = -1.0;
	strict1              = -1.0;
	strict2              = -1.0;
	barnums              = -1;
	make_index           =  0;
	make_audio           =  0;
	playtempo            =  0;


	select_all           = 0;
	pagenumbers          = 0;
	strcpy (styf, "");
	strcpy (transpose, "");
	strcpy (vcselstr, "");
	strcpy (au_qstr, "");

	if (job) {
		strcpy (styd, DEFAULT_FDIR);
		strcpy (outf, OUTPUTFILE);
		interactive          = 0;
		pretty               = 0;
		epsf                 = 0;
		choose_outname       = 0;
		gmode                = G_FILL;
		search_field0        = S_TITLE;
	}
}


/* ----- ops_into_fmt ----- */
void ops_into_fmt (fmt)
	struct FORMAT *fmt;
{
	if (landscape >= 0)        fmt->landscape=landscape;
	if (slurgraces >= 0)       fmt->slurgraces=slurgraces;
	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 (one_per_page >= 0)     fmt->oneperpage=one_per_page;
	if (alfa_c >= 0)           fmt->maxshrink=alfa_c;
	if (staffsep >= 0)         fmt->staffsep=staffsep;
	if (strict1 >= 0)          fmt->strict1=strict1;
	if (strict2 >= 0)          fmt->strict2=strict2;
	if (barnums >= 0)          fmt->barnums=barnums;
	if (playtempo > 0)         fmt->playtempo=playtempo;
	if (strlen(au_qstr)>0)     strcpy(fmt->auquality, au_qstr);
	fmt->staffsep    = fmt->staffsep    + dstaffsep;
	fmt->sysstaffsep = fmt->sysstaffsep + dstaffsep;

}

char aaa[201];
char ext[41];
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Process the +/-o command line option.  The possibilities are:
*   -o        means no PS output.
*   +o        means PS output to stdout.
*   +o<file>  means PS output to <file>
*   +o <file> means PS output to <file>
* In the last case, the next arg will be treated as a file name if it  doesn't
* start with '+' or '-'.
*
* The return value is either ac or ac+1, depending on whether we use the  next
* arg as the file name.
*/
int Opt_o(a,ac,av)
	int   a;
	int   ac;
	char**av;
{	char *F="Opt_o";
	int   c0 = av[a][0];
	int   c1 = av[a][1];
	int   c;
	V6 "%s: Opt '%c%c' \"%s\"\n",F,c0,c1,av[a] V;
	if (c0 == '+') {
		V6 "%s: Enable PS output.\n",F V;
		do_mode = DO_OUTPUT;
		make_ps = 1;
		use_stdout = 0;	// Not writing to stdout
		if (av[a][2]) {
			V6 "%s +o with file name \"%s\"\n",F,&av[a][2] V;
			strcpy (aaa, &av[a][2]);
			strcpy(outf, aaa);
			strext(outf, aaa, "ps", 1);
		} elsif (a+1 < ac && (c = av[a+1][0]) && c != '-' && c != '+') {
			++a;
			strcpy (aaa, &av[a][0]);
			V6 "%s +o plus file arg \"%s\"\n",F,aaa V;
			if (!strcmp(aaa,"=")) {
				choose_outname=1;
				V6 "%s: Automatic choice of output name.\n",F V;
			} else {
				V6  "%s: Output file \"%s\"\n",F,aaa V;
				getext (aaa,ext);
				if (strcmp(ext,"ps") && strcmp(ext,"eps") && strcmp(ext,"")) {
					fprintf(stderr,"Wrong extension for output file: %s\n", aaa);
					return 1;
				}
				strcpy(outf, aaa);
				if (epsf || strcmp(ext,"eps") == 0) {
					strext(outf, aaa, "eps", 1);
					epsf = 1;	// Note the request for EPS
				} else {
					strext(outf, aaa, "ps", 1);
				}
				V2 "%s: outf=\"%s\"\n",F,outf V;
				choose_outname=0;
			}
		} else {
			V6 "%s +o with no arg.\n",F V;
		}
		V5 "%s: Output PS file \"%s\"\n",F,outf V;
	} elsif (c0 == '-') {
		V6 "%s: Disable PS output.\n",F V;
		do_mode = 0;
		make_ps = 0;
	} else {

	}
	return a;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* parse_args: parse list of arguments, interpret flags.
* The return value is an error code; if nonzero, we will exit.
*/
int parse_args(ac, av)
	int   ac;
	char *av[];
{	char *F = "parse_args";
	int   a,i,l,m,k,nsel,sel_arg,j,ok,f_pos,got_value;
	int   c0, c1, c2;
	char  c;

	V6 "%s: Called with %d args.\n",F,ac V;
	help_me = 0;
	ninf = 0;
	nsel = 0;
	sel_arg = 0;
	f_pos = -1;
	strcpy(sel_str[0], "");
	s_field[0] = search_field0;

	for (a=1; a<ac; a++) {
		V6 "%s: Arg %d \"%s\"\n",F,a,av[a] V;
		c0 = av[a][0];
		switch(c0) {
		default:			// Not an option string
			V3 "Arg \"%s\"\n",av[a] V;
			l = strlen(av[a]);
			if ((l > 3) && !strcmp(av[a]+l-4,".fmt")) {
				V3 "Arg \"%s\" is format file.\n",av[a] V;
				strcpy(styf, av[a]);
			} else {
				V3 "Arg \"%s\" is input file.\n",av[a] V;
				use_stdin = 0;		// Not reading stdin
				if (sel_arg && !strcmp(av[a]+l-4,".abc")) {     /* Input file if .abc */
					V4 "Arg \"%s\" is .abc file.\n",av[a] V;
					nsel++;
					strcpy(sel_str[nsel], "");
					s_field[nsel] = S_TITLE;
					V4 "s_field[%d]=%d=S_TITLE\n",nsel,s_field[nsel] V;
					if (ninf == 0) {
						V4 "Selector before first file.\n" V;
						strcpy(sel_str[nsel],sel_str[nsel-1]);
						V3 "sel_str[%d]=\"%s\"\n",nsel,sel_str[nsel] V;
						s_field[nsel]=s_field[nsel-1];
						V4 "s_field[%d]=%d\n",nsel,s_field[nsel] V;
					}
					sel_arg=0;
				}
				if (is_xrefstr(av[a]) && (!sel_arg)) {    /* sel if xref numbers */
					V4 "Arg \"%s\" is xref number.\n",av[a] V;
					if (a-1 != f_pos) sel_arg=1;
				}
				if (!sel_arg) {             /* this arg is a file name */
					V4 "Arg \"%s\" is file name.\n",av[a] V;
					use_stdin = 0;			// Not reading stdin
					if (ninf >= MAXINF) {
						V1 "%s: Too many input files, max is %d\n",myname,MAXINF V;
						return 1;
					}
					strcpy(in_file[ninf], av[a]);
					psel[ninf] = nsel;
					V3 "Input file %d is \"%s\"\n",ninf,in_file[ninf] V;
					ninf++;
				} else {                             /* this arg is a selector */
					V3 "Arg \"%s\" is selector %d.\n",av[a],nsel V;
					strcat(sel_str[nsel], av[a]);
					strcat(sel_str[nsel], " ");
				}
			}
			V5 "sel_arg=%d.\n",sel_arg V;
			continue;

		case '-':			// Option strings
		case '+':
			c1 = av[a][1];
			V3 "Opt \"%s\" c0='%c' c1='%c'\n",av[a],c0,c1 V;
			switch(c1) {		// New, revised options

			case '1':
				one_per_page = (c0 == '+') ? 1 : 0;
				continue;

			case 'B':			// Bars per line
				if (c0 == '+') {
					if ((c2 = av[a][2]) && isdigit(c2)) {
						sscanf(av[a]+2,"%d",&bars_per_line);
					} elsif (av[a+1] && (c2 = av[a+1][0]) && isdigit(c2)) {
						sscanf(av[++a],"%d",&bars_per_line);
					} else {
						V2 "%s: \"%s\" needs a numeric value.\n",mypath,av[a] V;
					}
					continue_lines = 0;
				} else {
					bars_per_line = 0;
				}
				continue;

			case 'E':		// EPS output; file name should follow
				epsf = (c0 == '+') ? 1 : 0;
				a = Opt_o(a,ac,av);
				continue;

			case 'e':
				sel_arg = (c0 == '+') ? 1 : 0;
				V5 "sel_arg=%d.\n",sel_arg V;
				continue;

			case 'F':			// Format file
				V6 "%s: Format option.\n",F V;
				if (c0 == '+') {
					if (av[a][2]) {		// +F<file>
						strcpy(styf, av[a]+2);
						V3 "Use format file -F\"%s\"\n",styf V;
						continue;
					} else {			// Try next arg
						if (a+1 < ac) {	// +F <file>
							strcpy(styf, av[++a]);
							V3 "Use format file av[%d]=\"%s\"\n",a,styf V;
							continue;
						} else {
							V1 "+F option has no file name.\n" V;
							continue;
						}
					}
				} else {
					V3 "Don't read format file.\n" V;
					strcpy(styf, "");
					continue;
				}
				continue;

			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;
				V5 "sel_arg=%d f_pos=%d.\n",sel_arg,f_pos V;
				f_pos=a;
				continue;

			case 'H':
				help_me = (c0 == '+') ? 2 : 0;
				continue;

			case 'h':
				write_help();
				continue;

			case 'N':
				pagenumbers = (c0 == '+') ? 1 : 0;
				continue;

			case 'P':
				pretty = (c0 == '+') ? 2 : 0;
				continue;

			case 'p':
				pretty = (c0 == '+') ? 1 : 0;
				continue;

			case 'a':
				if (c0 == '+') {
					do_mode = DO_OUTPUT;
					make_audio = 1;
				} else {
					make_audio = 0;
				}
				V3 "%srite audio; do_mode=%d.\n",(make_audio?"W":"Don't w"),do_mode V;
				continue;

			case 'b':			// Break lines
				if (c0 == '+') {		// Ignore line breaks
					break_continues= 1;	// Ignore continues (/)
					continue_lines = 0;	// All EOLs are staff break
				} else {				// Respect line breaks
					break_continues= 0;	// Honor continues
				}
				V3 "%s line breaks.\n",(continue_lines?"Ignore":"Honor") V;
				V3 "%s continues.\n",(break_continues?"Ignore":"Honor") V;
				continue;

			case 'c':			// Continue lines
				if (c0 == '+') {		// Ignore line breaks
					continue_lines = 1;	// Treat all lines as continued
					break_continues= 0;	// Honor existing continues
				} else {				// Respect line breaks
					continue_lines = 0;	// Honor line/staff breaks
				}
				V3 "%s line breaks.\n",(continue_lines?"Ignore":"Honor") V;
				V3 "%s continues.\n",(break_continues?"Ignore":"Honor") V;
				continue;

			case 'I':
				make_index = (c0 == '+') ? 1 : 0;
				continue;

			case 'i':			// Interactive
				interactive = (c0 == '+') ? 1 : 0;
				V3 "%snteractive.\n",(interactive?"I":"Not i") V;
				continue;

			case 'l':
				landscape = (c0 == '+') ? 1 : 0;
				continue;

			case 'n':			// Print notes
				write_history = (c0 == '+') ? 1 : 0;
				V3 "%s notes.\n",(write_history?"Show":"Ignore") V;
				continue;

			case 'o':			// Output
				V6 "%s: Output option.\n",F V;
				a = Opt_o(a,ac,av);
				continue;

			case 'x':			// Print index numbers
				include_xrefs = (c0 == '+') ? 1 : 0;
				V3 "%s X index numbers.\n",(include_xrefs?"Show":"Ignore") V;
				continue;

			case 'T':
				s_field[nsel]=S_TITLE;
				sel_arg=1;
				V5 "sel_arg=%d s_field[%d]=%d=S_TITLE.\n",sel_arg,nsel,s_field[nsel] V;
				continue;

			case 'C':
				s_field[nsel]=S_COMPOSER;
				sel_arg=1;
				V5 "sel_arg=%d s_field[%d]=%d=S_COMPOSER.\n",sel_arg,nsel,s_field[nsel] V;
				continue;

			case 'R':
				s_field[nsel]=S_RHYTHM;
				sel_arg=1;
				V5 "sel_arg=%d s_field[%d]=%d=S_RHYTHM.\n",sel_arg,nsel,s_field[nsel] V;
				continue;

			case 'S':
				s_field[nsel]=S_SOURCE;
				sel_arg=1;
				V5 "sel_arg=%d s_field[%d]=%d=S_SOURCE.\n",sel_arg,nsel,s_field[nsel] V;
				continue;

			case 'm':
				if (!strcmp(av[a]+1,"maxv")) {
					if ((a==ac-1)||(av[a+1][0]=='-')) {
						rx("missing parameter for ",av[a]);
					}
					sscanf(av[++a],"%d",&maxVc);
					V2 "maxVc=%d.\n",maxVc V;
					if (maxVc > MAXV) {
						maxVc = MAXV;
						V2 "maxVc=%d [MAXV=%d limit].\n",maxVc,MAXV V;
					}
				} elsif (!strcmp(av[a]+1,"maxs")) {
					if ((a==ac-1)||(av[a+1][0]=='-')) {
						rx("missing parameter for ",av[a]);
					}
					sscanf(av[++a],"%d",&maxSyms);
					V2 "maxSyms=%d.\n",maxSyms V;
				}
				continue;

			default:
				V3 "Opt \"%s\" ...\n",av[a] V;
				m = 1;				// Char position within option string
				k = strlen(av[a]);	// Length of option string
				while (m < k) {		// Run down option string
					c = av[a][m];	// Treat each char as option
					V3 "Opt \"%s\" char %d='%c'...\n",av[a]+m,m,c V;
					if (strchr("vsdwmgtkqQDFYBVXO",c)) {  /* flags with parameter.. */
						V3 "Look for parameter for option '%c' ...\n",c V;
						strcpy(aaa, &av[a][m+1]);
						V3 "av[%d][%d] is \"%s\" ...\n",a,m+1,aaa V;
						if ((strlen(aaa)>0) && strchr("glO",c)) {     /* no sticky arg */
							fprintf(stderr,"+++ Incorrect usage of flag -%c\n", c);
							return 1;
						}
						got_value=1;                              /* check for value */
						if (strlen(aaa)==0) {
							got_value=0;
							a++;
							if ((a>=ac) || (av[a][0]=='-'))
								a--;
							else {
								strcpy(aaa,av[a]);
								got_value=1;
							}
						}
						if (got_value && strchr("vskYB",c)) {      /* check num args */
							ok=1;
							for (j=0;j<strlen(aaa);j++)
								if (!strchr("0123456789.",aaa[j])) ok=0;
							if (!ok) {
								V1 "Invalid parameter <%s> for flag -%c\n",aaa,c V;
								return 1;
							}
						}
						/*
						--- next ops require a value ---
						*/
						if (!got_value) {                   /* check value was given */
							fprintf(stderr,"+++ Missing parameter after flag -%c\n", c);
							return 1;
						}
						if (c=='k') {
							sscanf(aaa,"%d",&barnums);
							V1 "Bar numbers every %d bars for flag %c%c \"%s\"\n",barnums,c0,c1,aaa V;
							break;
						}
						if (c=='Q') {
							sscanf(aaa,"%f",&playtempo);
							if (playtempo < 6) playtempo=6;
							break;
						}
						if (c=='q') {
							strcpy (au_qstr, aaa);
							break;
						}
						if (c=='V') {                               /* -V flag */
							ok=1;
							strcpy (vcselstr, aaa);
						}
						if (c=='X') {                               /* -X flag */
							ok=1;
							if (aaa[0]==',') {
								sscanf(aaa,",%f",&strict2);
								if (strict2<-0.001 || strict2>1.001) ok=0;
							} elsif (strchr(aaa,',')) {
								sscanf (aaa,"%f,%f",&strict1,&strict2);
								if (strict1<-0.001 || strict1>1.001) ok=0;
								if (strict2<-0.001 || strict2>1.001) ok=0;
							} else {
								sscanf(aaa,"%f",&strict1);
								if (strict1<-0.001 || strict1>1.001) ok=0;
								strict2=strict1;
							}
							if (!ok) {
								fprintf(stderr,"+++ Invalid parameter <%s> for flag -%c\n",aaa,c);
								return 1;
							}
							V2 "\n%s: strict1=%.2f strict2=%.2f.\n",F,strict1,strict2 V;
						}
						if (c=='o') {                               /* -O flag  */
							if (!strcmp(aaa,"=")) {
								choose_outname=1;
							} else {
								getext (aaa,ext);
								if (strcmp(ext,"ps") && strcmp(ext,"eps") && strcmp(ext,"")) {
									fprintf(stderr,"Wrong extension for output file: %s\n", aaa);
									return 1;
								}
								strext (outf, aaa, "ps", 1);
								choose_outname=0;
							}
						}
						if (c=='B') {
							sscanf(aaa,"%d",&bars_per_line);
							continue_lines=0;
						}
						if (c=='t') strcpy(transpose,aaa);
						if (c=='v') sscanf(aaa,"%d",&vb);
						if (c=='s') sscanf(aaa,"%f",&scalefac);
						if (c=='d') {
							if (aaa[0]=='+' || aaa[0]=='-') dstaffsep = scan_u(aaa);
							else staffsep = scan_u(aaa);
						}
						if (c=='w') swidth   = scan_u(aaa);
						if (c=='m') lmargin  = scan_u(aaa);
						if (c=='D') strcpy (styd,aaa);
						if (c=='g') {
							if      (abbrev(aaa,"shrink", 2)) gmode=G_SHRINK;
							elsif (abbrev(aaa,"stretch",2)) gmode=G_STRETCH;
							elsif (abbrev(aaa,"space",  2)) gmode=G_SPACE;
							elsif (abbrev(aaa,"fill",   2)) gmode=G_FILL;
							else {
								fprintf(stderr,"+++ Bad parameter for flag -g: %s\n",aaa);
								return 1;
							}
						}
						break;
					} else {
						V1 "Unknown flag: %c%c\n",c0,c V;
						return 1;
					}
					m++;
				}
				continue;

			}
		}
	}
	return 0;
}

/* ----- alloc_structs ----- */
/* Thanks to Henrik Norbeck for this */
void alloc_structs ()
{
	int j;

	sym = (Sym *)calloc(maxSyms, sizeof(Sym));
	if (sym==NULL) rx("Out of memory","");

	symv = (Sym **)calloc(maxVc, sizeof(Sym *));
	if (symv==NULL) rx("Out of memory","");

	for (j=0;j<maxVc;j++) {
		symv[j] = (Sym *)calloc(maxSyms, sizeof(Sym));
		if (symv[j]==NULL) rx("Out of memory","");
	}

	xp = (struct XPOS *)calloc(maxSyms+1, sizeof(struct XPOS));
	if (xp==NULL) rx("Out of memory","");

	for (j=0;j<maxSyms+1;j++) {
		xp[j].p = (int *)calloc(maxVc, sizeof(int));
		if (xp[j].p==NULL) rx("Out of memory","");
	}

	voice=(Voice *)calloc(maxVc, sizeof(Voice));
	if (voice==NULL) rx("Out of memory","");

	sym_st = (Sym **)calloc(maxVc, sizeof(Sym *));
	if (sym_st==NULL) rx("Out of memory","");

	for (j=0;j<maxVc;j++) {
		sym_st[j] = (Sym *)calloc(MAXSYMST, sizeof(Sym));
		if (sym_st[j]==NULL) rx("Out of memory","");
	}

	nsym_st = (int *)calloc(maxVc, sizeof(int));
	if (nsym_st==NULL) rx("Out of memory","");

}


/* ----- set_page_format ----- */
int set_page_format ()
{	char *F="set_page_format";
	int i,j;

	V5 "%s: Called.\n",F V;
	if (pretty==1) {
		set_pretty_format (&cfmt);
	} elsif (pretty==2) {
		set_pretty2_format (&cfmt);
	} else {
		set_standard_format (&cfmt);
	}
//	i = read_fmt_file("fonts.fmt", styd, &cfmt);
//	Hard-wire some of the most common fonts:
	interpret_format_line("font AvantGarde-Demi", &cfmt);
	interpret_format_line("font Bookman-Demi",    &cfmt);
	interpret_format_line("font Courier",         &cfmt);
	interpret_format_line("font Gothic",          &cfmt);
	interpret_format_line("font Helvetica",       &cfmt);
	interpret_format_line("font Helvetica-Narrow",&cfmt);
	interpret_format_line("font Palatino",        &cfmt);
	interpret_format_line("font Times",           &cfmt);
	interpret_format_line("font Times-Narrow",    &cfmt);
	interpret_format_line("font Times-Courier",   &cfmt);
	interpret_format_line("font Times-New-Roman", &cfmt);
	interpret_format_line("font Times-Roman",     &cfmt);
//
	j = 0;
	if (strlen(styf)>0) {
		strext(styf,styf,"fmt",1);
		j=read_fmt_file (styf, styd, &cfmt);
		if (j==0)  {
			fprintf(stderr,"\n+++ Cannot open file: %s\n", styf);
			return 0;
		}
		strcpy(cfmt.name, styf);
	}
	if (verbose > 1) if (i || j) fprintf(stderr,"\n");
	ops_into_fmt (&cfmt);
	make_font_list (&cfmt);
	sfmt=cfmt;
	dfmt=cfmt;
	V5 "%s: Done.\n",F V;
	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 (str,s,wid)
	char *str;
	char s[];
	float *wid;
{	char *F="tex_str";
	char *cp, c;
	int   base,add,m,n;
	char  t[21];
	float w;

	cp=str;
	strcpy(s,"");
	n=0;
	w=0;
	while (*cp != '\0') {

		if ((*cp=='(') || (*cp==')'))           /* ( ) becomes \( \) */
			{sprintf(t, "\\%c", *cp); strcat(s,t); w+=cwid('('); n++; }

		elsif (*cp=='\\') {		// backslash sequences */
			if ((m = *++cp) == '\0') break;
			add=0;				// accented vowels */
			if (*cp=='`')  add=1;
			if (*cp=='\'') add=2;
			if (*cp=='^')  add=3;
			if (*cp=='"')  add=4;
			if (add) {
				if ((c = *++cp) == '\0') break;
				base=0;
				switch (*cp) {
				case 'a': base=340; if (add==4) add=5; break;
				case 'e': base=350; break;
				case 'i': base=354; break;
				case 'o': base=362; if (add==4) add=5; break;
				case 'u': base=371; break;
				case 'y': if (m == '\'') {base=374;} 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;
				case 'Y': if (m == '\'') {base=332;} break;
				}
				V5 "%s: Mark m=%02d='%c' c=%02d='%c' base=%d add=%d\n",F,m,m,c,c,base,add V;
				w+=cwid(*cp);
				if (base)
					{sprintf(t,"\\%d",base+add-1); strcat(s,t); n+=1; }
				else
					{ switch (add) {
					case 1: sprintf(t,"\\221%c",*cp); strcat(s,t); n+=1; break;
					case 2: sprintf(t,"\\222%c",*cp); strcat(s,t); n+=1; break;
					case 3: sprintf(t,"\\223%c",*cp); strcat(s,t); n+=1; break;
					case 4: sprintf(t,"\\230%c",*cp); strcat(s,t); n+=1; break;
					}
				}
			} elsif (*cp==' ')                              /* \-space */
				{ strcat(s," "); w+=cwid(' '); n++; }
			elsif (*cp=='-')                                /* macron */
				{ strcat(s,"\\225"); }
			elsif (*cp=='=')                                /* macron */
				{ strcat(s,"\\225"); }
			elsif (*cp=='.')                                /* dot-accent */
				{ strcat(s,"\\227"); }
			elsif (*cp=='u')                                /* breve */
				{ strcat(s,"\\226"); }
			elsif (*cp=='v')                                /* hacek/caron */
				{ strcat(s,"\\237"); }
			elsif (*cp=='k')                                /* ogonek */
				{ strcat(s,"\\236"); }
//			elsif (*cp=='?')                                /* ring above */
//				{ strcat(s,"\\232"); }*/
//			elsif (*cp=='?')                                /* double acute */
//				{ strcat(s,"\\235"); }*/
			elsif (*cp=='d')                                /* Croatian d */
				{ strcat(s,"\\001d"); n++; }
			elsif (*cp=='D')                                /* Croatian D */
				{ strcat(s,"\\002D"); n++; }
//			elsif (*cp=='i')                                /* Turkish undotted i */
//				{ strcat(s,"\\365"); n++; }
//			elsif (*cp=='I')                                /* Turkish dotted I */
//				{ strcat(s,"\\xxx"); n++; }
//			elsif (*cp=='L')                                /* L-slash */
//				{ strcat(s,"\\326"); w+=cwid('L'); n++; }
//			elsif (*cp=='l')                                /* l-slash */
//				{ strcat(s,"\\367"); w+=cwid('t'); n++; }
//----- - -
			elsif((*cp=='s')&&(*(cp+1)=='s'))               /* sz */
				{ cp++; strcat(s,"\\337"); w+=cwid('s'); n++; }
//----- Aa + o
			elsif((*cp=='a')&&(*(cp+1)=='a'))               /* a-ring */
				{ cp++; strcat(s,"\\345"); w+=cwid('a'); n++; }
			elsif((*cp=='A')&&(*(cp+1)=='A'))               /* A-ring */
				{ cp++; strcat(s,"\\305"); w+=cwid('A'); n++; }
//----- Aa
			elsif((*cp=='a')&&(*(cp+1)=='e'))               /* ae */
				{ cp++; strcat(s,"\\346"); w+=1.5*cwid('a'); n++; }
			elsif((*cp=='A')&&(*(cp+1)=='E'))               /* AE */
				{ cp++; strcat(s,"\\306"); w+=1.5*cwid('A'); n++; }
//***** Oo + e
			elsif((*cp=='o')&&(*(cp+1)=='e'))               /* oe */
				{ cp++; strcat(s,"\\234"); w+=1.5*cwid('o'); n++; }
			elsif((*cp=='O')&&(*(cp+1)=='E'))               /* OE */
				{ cp++; strcat(s,"\\214"); w+=1.5*cwid('O'); n++; }
//***** / + Oo
			elsif((*cp=='/')&&(*(cp+1)=='o'))               // \/o
				{ cp++; strcat(s,"\\370"); w+=1.5*cwid('o'); n++; }
			elsif((*cp=='/')&&(*(cp+1)=='O'))               // \/O
				{ cp++; strcat(s,"\\330"); w+=1.5*cwid('O'); n++; }
//----- O o
			elsif (*cp=='o')                                /* \o = o-slash */
				{ strcat(s,"\\370"); w+=cwid('o'); n++; }
			elsif (*cp=='O')                                /* \O = O-slash */
				{ strcat(s,"\\330"); w+=cwid('O'); n++; }
//----- c,
			elsif (*cp==',') {			/* c-cedilla */
				cp++;
				w+=cwid(*cp);
				if    (*cp=='C') {strcat(s,"\\307"); n++;}
				elsif (*cp=='c') {strcat(s,"\\347"); n++;}
				else            {sprintf(t,"\\233%c",*cp); strcat(s,t); n++;}
			} elsif (*cp=='c') {			/* c-cedilla */
				cp++;
				w+=cwid(*cp);
				if    (*cp=='C') {strcat(s,"\\307"); n++;}
				elsif (*cp=='c') {strcat(s,"\\347"); n++;}
				else            {sprintf(t,"%c",*cp); strcat(s,t); n++;}
			} elsif (*cp=='~') {			/* n-twiddle */
				cp++;
				w+=cwid(*cp);
				if      (*cp=='N') { strcat(s,"\\321"); n++; }
				elsif (*cp=='n') { strcat(s,"\\361"); n++; }
		/*
		 *  cd: changed default behaviour in order to enable escaping of '~'
		 *		else            { sprintf(t,"%c",*cp); strcat(s,t); n++; }
		 */
/* mr: I don't see why escaping of ~ would be necessary, trying it as an accent
				else            { sprintf(t,"~%c",*cp); strcat(s,t); n++; } */
				else            { sprintf(t,"\224%c",*cp); strcat(s,t); n++; }
			} else {	// \-something-else; pass through
				sprintf(t,"\\%c",*cp);
				strcat(s,t);
				w += cwid('A');
				n++;
			}
		}
		elsif (*cp=='{')	// It looks like braces are stripped out
			;
		elsif (*cp=='}')
			;
		else {				/* other characters: pass though */
				sprintf(t,"%c",*cp);
				strcat(s,t); n++;
				w+=cwid(*cp);
			}

		cp++;
	}
	*wid = w;

	return n;
}


/* ----- put_str: output a string in postscript ----- */
void put_str(str)
	char *str;
{	char *F="put_str";
	char s[801];
	float w;

	tex_str(str,s,&w);
	PUT1("%s", s);
}

/* ----- set_font ----- */
void set_font (fp,font,add_bracket)
	FILE *fp;
	struct FONTSPEC font;
	int add_bracket;
{
	int i,fnum;

	fnum=-1;
	for (i=0;i<nfontnames;i++) {
		if (!strcmp(font.name,fontnames[i])) fnum=i;
	}
	if (fnum<0) {
		fprintf(stderr,"\n+++ Font \"%s\" not predefined; using first in list\n",
						font.name);
		fnum=0;
	}
	PUT3("%.1f %d F%d ", font.size, font.box, fnum)
	if (add_bracket) PUT0("(")
}

/* ----- set_font_str ----- */
void set_font_str (str,font)
	char str[];
	struct FONTSPEC font;
{
	int i,fnum;

	fnum=-1;
	for (i=0;i<nfontnames;i++) {
		if (!strcmp(font.name,fontnames[i])) fnum=i;
	}
	sprintf (str,"%.1f %d F%d ", font.size, font.box, 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 (title,fnm)
	char title[],fnm[];
{
	char *p,*q;

	p=title; q=fnm;
	while (*p != '\0') {
		if (*p == ' ')
			*q = '_';
		else
			*q = *p;
		p++; q++;
	}
	*q = '\0';
}

/* ----- close_output_file ------ */
void close_output_file ()
{
	int m;

	if (!file_open) return;

	if (interactive) fprintf(stderr,"(close %s)\n", outfnam);

	close_page(fout);
	close_ps (fout);
	fclose (fout);
	if (tunenum==0)
		fprintf(stderr,"+++ Warning: no tunes written to output file\n");
	if (vb >= 1) {
		if (outfnam && *outfnam) {
			m = get_file_size (outfnam);
			if (vb > 1 || pagenum > 1)
			fprintf(stderr,"Output written to %s (%d page%s, %d title%s)\n",
				outfnam,
				pagenum, (pagenum==1 ? "" : "s"),
				tunenum, (tunenum==1 ? "" : "s"));
			//	m, (m==1 ? "" : "s"));
		} else {
			if (vb > 1 || pagenum > 1)
			fprintf(stderr,"Output written to stdout (%d page%s, %d title%s)\n",
				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 (fnam,tstr)
	char fnam[],tstr[];
{	char*F="open_output_file";
	V6 "%s: Called with fnam=\"%s\" tstr=\"%s\"\n",F,fnam,tstr V;
	if (*fnam && !strcmp(fnam,outfnam)) {
		V6 "%s: Continue with same output file \"%s\".\n",F,outfnam V;
		return;
	}
	if (file_open) {
		V2 "%s: close_output_file()\n",F V;
		close_output_file();
	}
	if (interactive) fprintf(stderr,"(open \"%s\")\n", fnam);
	if (use_stdout) {
		fout = stdout;
		V2 "%s: Output file is now stdout.\n",F V;
	} else {
		strcpy(outfnam, fnam);
		V6 "%s: Output file is now \"%s\"\n",F,outfnam V;
		if (outfnam[0]) {
			V6 "%s: Open output file \"%s\"\n",F,outfnam V;
			if ((fout = fopen (outfnam,"w")) == NULL) {
				rx("Cannot open output file ", outf);
			}
		} else {
			V1 "%s: No output file name.\n",F V;
		}
	}
	pagenum=0;
	tunenum=tnum1=tnum2=0;
	file_open=1;
	file_initialized=0;

}



/* ----- open_index_file ------- */
void open_index_file (fnam)
	char fnam[];
{	char *F = "open_index_file";
	V5 "%s: Open index file \"%s\"\n",F,fnam V;
	if ((findex = fopen (fnam,"w")) == NULL)
		rx ("Cannot open index file: ", fnam);

	index_initialized=0;

}

/* ----- close_index_file ------- */
void close_index_file ()
{

	if (vb>=8) fprintf(stderr,"Close index file\n");

	close_index_page (findex);

	fclose (findex);

}

/* ----- add_to_text_block ----- */
void add_to_text_block (ln,add_final_nl)
	char ln[];
	int add_final_nl;
{
	char *c,*a;
	char word[MAXWLEN+1];
	int nt,nl;

	nt=ntxt;
	c=ln;

	for (;;) {
		while (*c==' ') c++;
		if (*c=='\0') break;
		a=word;
		while ((*c!=' ')&&(*c!='\0')&&(*c!='\n')) {
			nl=0;
			if ((*c=='\\')&&(*(c+1)=='\\')) {
				nl=1;
				c+=2;
				break;
			}
			*a=*c;
			c++;
			a++;

		}
		*a='\0';
		if (strlen(word)>MAXWLEN) {
			word[MAXWLEN-1]='\0';
			fprintf(stderr,"+++ Insanely long word truncated to %d chars: %s\n",
							MAXWLEN-1,word);
		}
		if (nt>=MAXNTEXT) {
			fprintf(stderr,"\n+++ Line %d: %s\n", linenum,ln);
			rx("Text overflow; increase MAXNTEXT and recompile.","");
		}
		if (strlen(word)>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 (fp,job)
	FILE *fp;
	int  job;
{	char *F = "write_text_block";
	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=1.0;
	if (strstr(cfmt.textfont.name,"Times-Roman"))    swfac=1.00;
	if (strstr(cfmt.textfont.name,"Times-Courier"))  swfac=1.05;
	if (strstr(cfmt.textfont.name,"Helvetica"))      swfac=1.10;
	if (strstr(cfmt.textfont.name,"Helvetica-Bold")) swfac=1.15;
	if (strstr(cfmt.textfont.name,"Palatino"))       swfac=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(") rshow\n")
		} elsif (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=0.60*mc*cfmt.textfont.size;
			}
			ftline0=textwidth/cfmt.staffwidth;
			/* revised estimate: assume some chars lost at each line end */
			nbreak=ftline0;
			textwidth=textwidth+5*nbreak*cwid('a')*swfac*cfmt.textfont.size;
			ftline=textwidth/cfmt.staffwidth;
			ntline=ftline+1.0;
			if (vb>=10)
				fprintf(stderr,"first estimate %.2f, revised %.2f\n", ftline0,ftline);
			if (vb >= 10) {
				fprintf(stderr,"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);
	strcpy (page_init,"");
	return;
}



/* ----- put_words ------- */
void put_words (fp)
	FILE *fp;
{	char *F = "put_words";
	int i,nw,n;
	char str[81];
	char *p,*q;

	set_font (fp,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;
					q++;
					p++;
					if (*p==' ') break;
					if (*(p-1)==':') break;
					if (*(p-1)=='.') break;
				}
				if (*p==' ') p++;
			}
			*q='\0';

			/* permit page break at empty lines or stanza start */
			nw=nwords(text[i]);
			if ((nw==0) || (strlen(str)>0)) buffer_eob(fp);

			if (nw>0) {
				if (strlen(str)>0) {
					PUT0("45 0 M (")
					put_str (str);
					PUT0(") lshow\n")
				}
				if (strlen(p)>0) {
					PUT0("50 0 M (")
					put_str (p);
					PUT0(") rshow\n")
				}
			}
		}
	}

	buffer_eob (fp);
	strcpy (page_init,"");

}

/* ----- put_text ------- */
void put_text (fp, type, str)
	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);

}

/* ----- put_history ------- */
void put_history (fp)
	FILE *fp;
{	char *F = "put_history";
	int i,ok;
	float baseskip,parskip, bookskip;

	set_font (fp, 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 (strlen(info.rhyth)>0) {
		Bskip(baseskip);
		PUT0("0 0 M (Rhythm: ")
		put_str (info.rhyth);
		PUT0(") show\n")
		Bskip(parskip);
	}

	bookskip = 0.5;
	for (i=0; i<info.nbook; i++) {
		if (strlen(info.book[i])>0) {
			Bskip(bookskip*CM);
			bookskip = 0.3;
			PUT0("0 0 M (Book: ")
			put_str(info.book[i]);
			PUT0(") show\n")
		}
		Bskip(parskip);
	}

	if (strlen(info.src)>0) {
		Bskip(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_N, "");
	put_text (fp, TEXT_Z, "Transcription: ");

	ok=0;
	for (i=0;i<ntext;i++) {
		if (text_type[i]==TEXT_H) {
			Bskip(0.5*CM);
			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 (fp)
	FILE *fp;
{
	char t[201];

	if    (numtitle==1) strcpy (t,info.title);
	elsif (numtitle==2) strcpy (t,info.title2);
	elsif (numtitle==3) strcpy (t,info.title3);
	elsif (numtitle==4) strcpy (t,info.title4);
	elsif (numtitle==5) strcpy (t,info.title5);
	elsif (numtitle==6) strcpy (t,info.title6);
	if (vb>15) fprintf(stderr,"write inside title <%s>\n", t);
	if (strlen(t)==0) return;

	Bskip(cfmt.subtitlefont.size+0.2*CM);
	set_font (fp, cfmt.subtitlefont, 0);

	if (cfmt.titlecaps) cap_str(t);
	PUT0(" (")
	put_str (t);
	if (cfmt.titleleft) {
		PUT0(") 0 0 M rshow\n")
	} else {
		PUT1(") %.1f 0 M cshow\n", cfmt.staffwidth/2)
	}
	Bskip(cfmt.musicspace+0.2*CM);

}


/* ----- write_tunetop ----- */
void write_tunetop(fp)
	FILE *fp;
{

	PUT2("\n\n%% --- tune %d %s\n", tunenum+1, info.title)
	if (!epsf) Bskip(cfmt.topspace);
}


/* ----- write_tempo ----- */
void write_tempo(fp,tempo,meter)
	FILE *fp;
	char tempo[];
	Msig meter;
{	char *F = "write_tempo";
	char *r, *q;
	char text[STRL];
	int top,bot,value,len,i,err,fac,dig,div;
	Sym s;
	float stem,dotx,doty,sc,dx;

	if (vb>15) fprintf(stderr,"write tempo <%s>\n", info.tempo);
	r=tempo;
	set_font (fp,cfmt.tempofont,0);
	PUT0(" 18 0 M\n")

	for (;;) {
		V6 "%s: r=\"%s\"\n",F,r V;
		while (*r==' ') r++;                    /* skip blanks */
		if (*r=='\0') break;
		Q = r;		// Make sure this is defined, for syntax() calls
		if (*r=='"') {                          /* write string */
			V6 "%s: r=\"%s\" (text)\n",F,r V;
			r++;
			q=text;
			while (*r!='"' && *r!='\0') { *q=*r; r++; q++; }
			if (*r=='"') r++;
			*q='\0';
			if (strlen(text)>0) {
				PUT0("6 0 rmoveto (")
				put_str (text);
				PUT0(") rshow 12 0 \n")
			}
		} else {                                  /* write tempo denotation */
			V6 "%s: r=\"%s\"\n",F,r V;
			q=text;
			while (*r!=' ' && *r!='\0') { *q=*r; r++; q++; }
			*q='\0';
			q=text;
			len=QUARTER;
			value=0;
			err=0;
			if (strchr(q,'=')) {
				if (*q=='C' || *q=='c') {
					V6 "%s: Found '%c' (C)\n",F,*q V;
					q++;
					len = meter.dlen;
					V6 "%s: len=%d.\n",F,len V;
					div = 0;
					if (*q=='/') { div=1; q++; }
					V6 "%s: len=%d div=%d.\n",F,len,div V;
					fac=0;
					while (isdigit(*q)) {
						dig = *q - '0';
						fac = 10 * fac + dig;
						q++;
					}
					V6 "%s: len=%d div=%d fac=%d.\n",F,len,div,fac V;
					if (div) {
						if (fac==0) fac=2;
						if (len%fac) fprintf(stderr,"Bad length divisor in tempo: %s", text);
						len=len/fac;
						V6 "%s: len/fac len=%d div=%d fac=%d.\n",F,len,div,fac V;
					} elsif (fac>0) {
						len=len*fac;
						V6 "%s: len*fac len=%d div=%d fac=%d.\n",F,len,div,fac V;
					}
					if (*q!='=') err=1;
					q++;
					if (!isdigit(*q)) err=1;
					sscanf(q,"%d", &value);
				} elsif (isdigit(*q)) {
					V4 "%s: Digit \"%s\"\n",F,q V;
					if (sscanf(q,"%d/%d=%d", &top,&bot,&value) == 3) {
						V4 "%s: 3-term tempo top=%d bot=%d value=%d.\n",F,top,bot,value V;
						len = (BASE * top) / bot;
					} elsif ((i = sscanf(q,"%d/%d",&top,&bot))) {
						value = 0;
						V4 "%s: 2-term tempo top=%d bot=%d value=%d.\n",F,top,bot,value V;
						len = (BASE * top) / bot;
					} else {
						err=1;
					}
				} else {
					err=1;
				}
			} else {
				if (sscanf(q,"%d/%d", &top,&bot) == 2) {
					value = 0;
					V4 "%s: 2-term tempo top=%d bot=%d value=%d.\n",F,top,bot,value V;
					len=(BASE*top)/bot;
				} elsif (isdigit(*q)) {
					sscanf(q,"%d", &value);
					top = bot = 0;
					V4 "%s: 1-term tempo top=%d bot=%d value=%d.\n",F,top,bot,value V;
				} else {
					err=1;
				}
			}
			V4 "%s: top=%d bot=%d value=%d.\n",F,top,bot,value V;
			if (err) {
				fprintf(stderr,"\n+++ invalid tempo specifier: %s\n", text);
			} else {				/* draw note=value */
				s.len=len;
				identify_note(&s,r);
				sc=0.55*cfmt.tempofont.size/10.0;
				PUT2("gsave %.2f %.2f scale 15 3 rmoveto currentpoint\n", sc,sc)
				if (s.head==H_OVAL)  PUT0("HD")
				if (s.head==H_EMPTY) PUT0("Hd")
				if (s.head==H_FULL)  PUT0("hd")
				dx=4.0;
				if (s.dots) {
					dotx=8; doty=0;
					if (s.flags>0) dotx=dotx+4;
					if (s.head==H_EMPTY) dotx=dotx+1;
					if (s.head==H_OVAL)  dotx=dotx+2;
					for (i=0;i<s.dots;i++) {
						PUT2(" %.1f %.1f dt", dotx, doty)
						dx=dotx;
						dotx=dotx+3.5;
					}
				}
				stem=16.0;
				if (s.flags > 1) stem += 3.0 * (s.flags - 1);
				V2 "%s: WHOLE=%d s.flags=%d stem=%.1f s.len=%d\n",F,WHOLE,s.flags,stem,s.len V;
				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;
				if (value > 0) {
					dx = (dx + 18) * sc;
					V4 "%s: PUT value=%d at dx=%.1f\n",F,value,dx V;
					PUT2(" grestore %.2f 0 rmoveto (=%d) rshow\n", dx,value)
				} else {
					dx = (dx + 10) * sc;
					PUT1(" grestore %.2f 0 rmoveto () rshow\n", dx)
				}
			}
		}
	}
}


/* ----- write_inside_tempo  ----- */
void write_inside_tempo (fp)
	FILE *fp;
{

	Bskip(cfmt.partsfont.size);
	write_tempo(fp,info.tempo,voice[ivc].meter);
	Bskip(0.1*CM);
}

/* ----- write_heading  ----- */
void write_heading (fp)
	FILE *fp;
{	char *F = "write_heading";
	float lwidth,down1,down2;
	int i,ncl;
	char t[201];

	lwidth=cfmt.staffwidth;

	/* write the main title */
	Bskip(cfmt.titlefont.size+cfmt.titlespace);
	set_font (fp,cfmt.titlefont,1);
	if (cfmt.withxrefs && xrefnum>0) PUT1("%d. ", xrefnum)
	strcpy (t,info.title);
	if (cfmt.titlecaps) cap_str(t);
	put_str (t);
	if (cfmt.titleleft) PUT0(") 0 0 M rshow\n")
	else PUT1(") %.1f 0 M cshow\n", lwidth/2)

	/* write second title */
	if (numtitle>=2) {
		Bskip(cfmt.subtitlespace+cfmt.subtitlefont.size);
		set_font (fp,cfmt.subtitlefont,1);
		strcpy (t,info.title2);
		if (cfmt.titlecaps) cap_str(t);
		put_str (t);
		if (cfmt.titleleft) PUT0(") 0 0 M rshow\n")
		else PUT1(") %.1f 0 M cshow\n", lwidth/2)
	}

	/* write third title */
	if (numtitle>=3) {
		Bskip(cfmt.subtitlespace+cfmt.subtitlefont.size);
		set_font (fp,cfmt.subtitlefont,1);
		strcpy (t,info.title3);
		if (cfmt.titlecaps) cap_str(t);
		put_str (t);
		if (cfmt.titleleft) PUT0(") 0 0 M rshow\n")
		else PUT1(") %.1f 0 M cshow\n", lwidth/2)
	}

	/* write fourth title */
	if (numtitle>=4) {
		Bskip(cfmt.subtitlespace+cfmt.subtitlefont.size);
		set_font (fp,cfmt.subtitlefont,1);
		strcpy (t,info.title4);
		if (cfmt.titlecaps) cap_str(t);
		put_str (t);
		if (cfmt.titleleft) PUT0(") 0 0 M rshow\n")
		else PUT1(") %.1f 0 M cshow\n", lwidth/2)
	}

	/* write fifth title */
	if (numtitle>=5) {
		Bskip(cfmt.subtitlespace+cfmt.subtitlefont.size);
		set_font (fp,cfmt.subtitlefont,1);
		strcpy (t,info.title5);
		if (cfmt.titlecaps) cap_str(t);
		put_str (t);
		if (cfmt.titleleft) PUT0(") 0 0 M rshow\n")
		else PUT1(") %.1f 0 M cshow\n", lwidth/2)
	}

	/* write sixth title */
	if (numtitle>=6) {
		Bskip(cfmt.subtitlespace+cfmt.subtitlefont.size);
		set_font (fp,cfmt.subtitlefont,1);
		strcpy (t,info.title6);
		if (cfmt.titlecaps) cap_str(t);
		put_str (t);
		if (cfmt.titleleft) PUT0(") 0 0 M rshow\n")
		else PUT1(") %.1f 0 M cshow\n", lwidth/2)
	}

	/* write composer, origin */
	if ((info.ncomp>0) || (strlen(info.orig)>0)) {
		set_font (fp,cfmt.composerfont,0);
		Bskip(cfmt.composerspace);
		ncl=info.ncomp;
		if ((strlen(info.orig)>0) && (ncl<1)) ncl=1;
		for (i=0;i<ncl;i++) {
			Bskip(cfmt.composerfont.size);
			PUT1("%.1f 0 M (", lwidth)
			put_str (info.comp[i]);
			if ((i==ncl-1)&&(strlen(info.orig)>0)) {
				put_str (" (");
				put_str (info.orig);
				put_str (")");
			}
			PUT0 (") lshow\n");
		}
		down1=cfmt.composerspace+cfmt.musicspace+ncl*cfmt.composerfont.size;
	} else {
		Bskip(cfmt.composerfont.size+cfmt.composerspace);
		down1=cfmt.composerspace+cfmt.musicspace+cfmt.composerfont.size;
	}
	Bskip(cfmt.musicspace);

	/* decide whether we need extra shift for parts and tempo */
	down2=cfmt.composerspace+cfmt.musicspace;
	if (strlen(info.parts)>0) down2=down2+cfmt.partsspace+cfmt.partsfont.size;
	if (strlen(info.tempo)>0) down2=down2+cfmt.partsspace+cfmt.partsfont.size;
	if (down2>down1) Bskip(down2-down1);

	/* write tempo and parts */
	if (strlen(info.parts)>0 || strlen(info.tempo)>0) {

		if (strlen(info.tempo)>0) {
			Bskip(-0.2*CM);
			PUT1 (" %.2f 0 T ", cfmt.indent*cfmt.scale)
			write_tempo(fp, info.tempo, default_meter);
			PUT1 (" %.2f 0 T ", -cfmt.indent*cfmt.scale)
			Bskip(-cfmt.tempofont.size);
		}

		if (strlen(info.parts)>0) {
			Bskip(-cfmt.partsspace);
			set_font (fp,cfmt.partsfont,0);
			PUT0("0 0 M (")
			put_str (info.parts);
			PUT0(") rshow\n")
			Bskip(cfmt.partsspace);
		}

		if (strlen(info.tempo)>0) Bskip(cfmt.tempofont.size+0.3*CM);

	}


}

/* ----- write_parts  ----- */
void write_parts (fp)
	FILE *fp;
{	char *F = "write_parts";
	if (strlen(info.parts)>0) {
		Bskip(cfmt.partsfont.size);
		set_font (fp, cfmt.partsfont,0);
		PUT0("0 0 M (")
		put_str (info.parts);
		PUT0(") rshow\n")
		Bskip(cfmt.partsspace);
	}
}
