static char rcsid[] = "$Id: chord.c,v 1.12 1992/08/25 03:35:33 putz Exp $";
/*	@(#)chord.c 1.0 91/07/01 */

#include "chord.h"

FILE *source_fd;
FILE *toc_file = NULL;		/* optional table of contents file */

unsigned char
	text_line[MAXLINE],	/* Lyrics Buffer */
	chord[MAXTOKEN],	/* Buffer for the name of the chord */
	title1[MAXLINE];	/* Holds the first title line */

char
	source[MAXTOKEN],
	directive[MAXTOKEN],	/* Buffer for the directive */
	mesg[MAXTOKEN];

char
	i_input;		/* Input line pointer */
char
	*text_font = DEF_TEXT_FONT,	/* Default font for text */
	*chord_font = DEF_CHORD_FONT,	/* Default font for chord */
	*used_fonts[MAXFONTS][MAXLINE],
	*current_file,
	*chord_line[MAXLINE];

char *notes[7] =  { "C", "D", "E", "F", "G", "A", "B" };

int
	c,			/* Current character in input file */
	i_chord,		/* Index to 'chord' */
	i_directive,		/* Index to 'directive' */
	i_text, 		/* Index to 'text_line' */
	in_chord,		/* Booleans indicating parsing status */
	lyrics_only = FALSE,
	auto_space = FALSE,	/* applies lyrics_only when no chords */
	left_foot_odd = -1,	/* -1 for all page numbers on the right */
				/* 0 for odd page numbers on the right */
				/* 1 for odd page numbers on the left */
	number_all = FALSE,	/* number the first page (set with -p 1) */
	page_label = 1,		/* page number for footers and index */
	i_chord_ov,		/* Overflow Booleans */
	i_directive_ov = FALSE,
	i_text_ov = FALSE,
	in_directive = FALSE,
	in_chordrc = FALSE,
	in_chorus = FALSE,
	has_directive = FALSE,
	has_chord = FALSE,
	title1_found = FALSE,
	debug_mode = FALSE,
	need_soc = FALSE,
	pagination = 1,		/* virtual pages per physical pages */
	transpose = 0,		/* transposition value */
	vpos,			/* Current PostScript position, in points */
	hpos,
	start_of_chorus,	/* Vertical positions of the chorus */
	end_of_chorus,
	chord_size = DEF_CHORD_SIZE, 	/* Default font size for 'chord_font' */
	text_size = DEF_TEXT_SIZE, 	/* Default font size for 'text_font' */
	grid_size = DEF_GRID_SIZE,
	n_pages = 1,		/* total page counter */
	v_pages = 1,		/* virtual pages */
	n_lines = 1,		/* line number in input file */
	song_pages = 1,		/* song page counter */
	blank_space = 0;	/* consecutive blank line counter */

float
	chord_inc,
	scale = 1.0,		/* Current scale factor */
	rotation = 0.0;		/* Current rotation */

extern int nb_chord, first_ptr;
extern struct chord_struct chordtab[MAX_CHORD];

extern char *optarg;
extern int optind, opterr;

/* --------------------------------------------------------------------------------*/
use_chord_font()
	{
	printf ("CHORD_FONT setfont\n");
	}

/* --------------------------------------------------------------------------------*/
set_chord_font()
	{
	printf ("/CHORD_FONT { /%s findfont %d scalefont } def\n", chord_font, chord_size);
	re_encode (chord_font);
	}

/* --------------------------------------------------------------------------------*/
use_text_font()
	{
	printf ("TEXT_FONT setfont\n");
	}
/* --------------------------------------------------------------------------------*/
set_text_font(size)
int size;
	{
	printf ("/TEXT_FONT { /%s findfont %d scalefont } def\n", text_font, size);
	re_encode (text_font);
	}

/* --------------------------------------------------------------------------------*/
do_help (command) 
char *command;
	{
	fprintf (stderr, "Usage: %s [options] file\n", command);
	fprintf (stderr, "Options:\n");
	fprintf (stderr, "	-c n               : set chord size [9]\n");
	fprintf (stderr, "	-C postsctipt_font : Set chord font\n");
	fprintf (stderr, "	-t n               : set text size [12]\n");
	fprintf (stderr, "	-T postsctipt_font : set text font\n");
	fprintf (stderr, "	-g n               : set chord grid size [30]\n");
	fprintf (stderr, "	-l                 : only print lyrics\n");
	fprintf (stderr, "	-h                 : This message\n");
	fprintf (stderr, "	-2                 : 2 pages per sheet\n");
	fprintf (stderr, "	-4                 : 4 pages per sheet\n");
	fprintf (stderr, "	-d                 : dumps default chords definitions\n");
	fprintf (stderr, "	-i file            : write table of contents to file\n");
	fprintf (stderr, "	-a                 : automatic single space lines without chords\n");
	fprintf (stderr, "	-p n               : starting page number [1]\n");
	fprintf (stderr, "	-R                 : odd pages numbers on right\n");
	fprintf (stderr, "	-L                 : odd pages numbers on left\n");
	}

/* --------------------------------------------------------------------------------*/
do_chord (i_text, chord)
int i_text;
char *chord;
	{
	if (transpose != 0)
		if (do_transpose (chord) != 0)
			{
			sprintf (mesg, "Don't know how to transpose [%s]", chord);
			error (mesg);
			}

	if (i_text < MAXLINE)
		chord_line[i_text] = chordtab[add_to_chordtab(chord)].chord_name;
	}

/* --------------------------------------------------------------------------------*/
print_chord_line ()
	{
	int i, j;		/* Counter */
	
	for (j=0; j<MAXLINE; j++)
		{
		if (chord_line[j] != NULL )
			{
			use_text_font();
			printf ("(");
			for (i=0; i < j; printf ("%c",text_line[i++]));
				printf (") stringwidth  pop %d add %d moveto\n", hpos, vpos);

			use_chord_font();
			printf ("(%s) show\n", chord_line[j]);
	
			chord_line[j]=NULL;
			}
		}
	}

/* --------------------------------------------------------------------------------*/
do_directive(directive)
char *directive;
	{
	int   i;
	char  *command, *comment;

	command=(char *)strtok(directive, ": ");

	if (!strcmp(command, "start_of_chorus") || !strcmp(command,"soc"))
		{
		/* start_of_chorus = vpos - blank_space; */
		need_soc = TRUE;
		in_chorus = TRUE;
		}
	else if (!strcmp (command, "end_of_chorus") || !strcmp(command, "eoc"))
		{
		if ( in_chorus )
			{
			end_of_chorus = vpos;
			do_chorus_line();
			in_chorus = FALSE;
			}
		else
			error ("Not in a chorus.");
		}
	else if (!strcmp (command, "textfont") || !strcmp (command, "tf"))
		{
		text_font = (char *)strtok(NULL, ": ");
		}
	else if (!strcmp (command, "chordfont") || !strcmp (command, "cf"))
		{
		chord_font = (char *)strtok(NULL, ": ");
		set_chord_font();
		}
	else if (!strcmp (command, "chordsize") || !strcmp (command, "cs"))
		{
		i = atoi((char *)strtok(NULL, ": "));
		if ( i == 0 )
			error ("invalid value for chord_size");
		else
			chord_size = i;
		set_chord_font();
		}
	else if (!strcmp (command, "textsize") || !strcmp (command, "ts"))
		{
		i = atoi((char *)strtok(NULL, ": "));
		if ( i == 0 )
			error ("invalid value for text_size");
		else
			text_size = i;
		}
	else if (!strcmp (command, "comment") || !strcmp (command, "c"))
		{
		comment = (char *)strtok(NULL, "\0");
		advance(blank_space);
		blank_space = 0;
		advance(text_size);
		text_line[i_text] = '\0';
		use_text_font();
		printf (".9  setgray\n");
		printf ("%d  setlinewidth\n", text_size);
		printf ("newpath\n");
		printf ("%d %d moveto\n", hpos, vpos + text_size/2 - 2);
		printf ("(%s) stringwidth rlineto\n", comment);
		printf ("stroke\n");
		printf ("%d %d moveto\n", hpos, vpos);
		printf ("0  setgray\n");
		printf ("1 setlinewidth\n");
		printf ("(%s) show\n", comment);
		i_text = 0;
		}
	else if (!strcmp(command, "new_song") || !strcmp(command,"ns"))
		{
		do_new_song();
		}
	else if (!strcmp(command, "title") || !strcmp(command,"t"))
		{
		do_title();
		}  
	else if (!strcmp(command, "subtitle") || !strcmp(command,"st"))
		{
		do_subtitle();
		}  
	else if (!strcmp(command, "define") || !strcmp(command,"d"))
		{
		do_define_chord();
		}  
	else
		{
		sprintf (mesg, "Invalid Directive : [%s]", command);
		error(mesg);
		has_directive = FALSE;
		}
	}

/* --------------------------------------------------------------------------------*/
do_chorus_line()
	{
	printf ("1  setlinewidth\n");
	printf ("newpath\n");
	printf ("%d %d moveto\n", L_MARGIN - 10, start_of_chorus);
	printf ("0 %d rlineto\n", -(start_of_chorus - end_of_chorus));
	printf ("closepath\n");
	printf ("stroke\n");
	}
/* --------------------------------------------------------------------------------*/
do_translate(vert, horiz)
	float vert, horiz;
	{
	printf ("%f %f translate\n", vert , horiz );
	debug ("changing translation");
	}
/* --------------------------------------------------------------------------------*/
do_new_song()
	{
	do_end_of_song();
	nb_chord=first_ptr=0;
	song_pages = 0;
	do_start_of_page();
	init_known_chords();
	read_chordrc();
	}
/* --------------------------------------------------------------------------------*/
init_ps()
	{
	printf ("%%!PS-Adobe-1.0\n");
	printf ("%%%%Title: A song\n");
	printf ("%%%%Creator: Martin Leclerc & Mario Dorion\n");
	printf ("%%%%Pages: (atend)\n");
	printf ("%%%%BoundingBox: 5 5 605 787\n");
	printf ("%%%%EndComments\n");
	printf ("/inch {72 mul } def\n");

	print_re_encode();
	set_chord_font();
	set_text_font(text_size);
	do_init_grid_ps();
	init_known_chords();

	printf ("%%%%EndProlog\n");

	printf ("%%%%Page: \"%d\" %d\n",n_pages, n_pages);
	printf ("%%%%BeginPageSetup\n");
	printf ("/pgsave save def\n");
	printf ("%f %f scale\n", scale, scale);
	printf ("%f rotate\n", rotation);
	printf ("%%%%EndPageSetup\n");

	vpos = TOP;
	hpos  = L_MARGIN;
	if (pagination==4)
		do_translate (0.0,   800.0);
	else if (pagination==2)
		do_translate (-40.0, -800.0);

	}

/* --------------------------------------------------------------------------------*/
do_end_of_song()
{
	draw_chords();
	do_end_of_page();
}
		 

/* --------------------------------------------------------------------------------*/
do_end_of_phys_page()
/* physical */
	{
	debug ("end_of_phys_page");
	printf ("pgsave restore\n");
	printf ("showpage\n");
	printf ("%%%%EndPage: \"%d\" %d\n",n_pages, n_pages);
	}

/* --------------------------------------------------------------------------------*/
do_end_of_page()
/* Logical */
	{
	printf ("1  setlinewidth\n");
	printf ("0  setgray\n");
	printf ("newpath\n");
	printf ("%d %d 10 sub moveto\n", L_MARGIN, BOTTOM); 
	printf ("%d 0 rlineto\n", WIDTH - L_MARGIN * 2);
	printf ("stroke\n");
	if (number_all || song_pages>1)
		{
		int pnum = (number_all ? page_label : song_pages);
		set_text_font(text_size - 2);
		use_text_font();
		if (page_label % 2 == left_foot_odd) {  /* left side */
			printf ("1 inch %d 3 div moveto\n", BOTTOM); 
			printf ("(Page %d) show\n", pnum);
		} else {                                /* right side */
			printf ("(Page %d) dup stringwidth pop\n", pnum);
			printf ("%d exch sub 1 inch sub %d 3 div moveto\n",
				WIDTH, BOTTOM); 
			printf ("show\n");
		}

		if (title1_found )
			{
			printf ("(%s) dup stringwidth pop 2 div\n", title1);
			printf ("%d 2 div exch sub %d 3 div moveto\n",
				WIDTH, BOTTOM);
			printf ("show\n");
			}
		set_text_font(text_size);
		}
	if ( in_chorus )
		{
		end_of_chorus = vpos;
		do_chorus_line();
		}

	if (v_pages == pagination) 
		{
		do_end_of_phys_page();
		v_pages = 0;
		}
	}

/* --------------------------------------------------------------------------------*/
do_start_of_page()
/*logical page ! */
	{
	v_pages++;
	page_label++;

	if (v_pages == 1)
		{
		n_pages++;
		printf ("%%%%Page: \"%d\" %d\n",n_pages, n_pages);
		printf ("%%%%BeginPageSetup\n");
		printf ("/pgsave save def\n");
		printf ("%f %f scale\n", scale, scale);
		printf ("%f rotate\n",rotation);
		printf ("%%%%EndPageSetup\n");
		}

	if (pagination==4) {
		if (v_pages==1) 
			do_translate(0.0, 800.0);
		else if (v_pages==2)
			do_translate(600.0, 0.0);
		else if (v_pages==3)
			do_translate(-600.0, -800.0);
		else if (v_pages==4)
			do_translate(600.0, 0.0);
	}

	if (pagination==2) {
		if (v_pages==1) 
			do_translate(-40.0, -800.0);
		else if (v_pages==2)
			do_translate(500.0, 0.0);
	}

	vpos = TOP;
	hpos=L_MARGIN;
	song_pages++;
	if ( in_chorus )
		{
		start_of_chorus = vpos;
		}
	}

/* --------------------------------------------------------------------------------*/
advance(amount)
int amount;
	{
	vpos = vpos - amount;     /* Affect text positionning ! */
	if (vpos < BOTTOM )
		{
		do_end_of_page();
		do_start_of_page();
		}
	}

/* --------------------------------------------------------------------------------*/
print_text_line()
	{
	int i;

	text_line[i_text] = '\0';

	for (i=0; text_line[i] == ' '; i++);

	if (!(lyrics_only || auto_space && !has_chord))
		{
		advance(blank_space);
		blank_space = 0;
		advance (chord_size + 1);

		if ( ( text_line[i] != '\0' )
		  && ( vpos - text_size <= BOTTOM))
			advance (text_size);

		if (need_soc)
			{
			start_of_chorus = vpos + chord_size;
			need_soc = FALSE;
			}
		print_chord_line();
		}

	if ( text_line[i] == '\0')
		{
		blank_space += text_size - 2;
		debug ("blank line found ");
		}
	else
		{
		advance (blank_space);
		blank_space = 0;
		advance (text_size - 1);
		if (need_soc)
			{
			start_of_chorus = vpos + text_size;
			need_soc = FALSE;
			}
		use_text_font();
		printf ("%d %d moveto\n", hpos, vpos);
		printf ("(%s) show\n", text_line);
		}

	i_text = 0;
	i_text_ov = FALSE;
	hpos = L_MARGIN;
	has_chord = FALSE;
	}

/* --------------------------------------------------------------------------------*/
do_title()
	{
	char *buf;
	buf = (char *)strtok(NULL, "\0");
	set_text_font (text_size+5);
	use_text_font();
	printf ("(%s) dup stringwidth pop 2 div\n", buf);
	printf ("%d 2 div exch sub %d moveto\n", WIDTH, vpos);
	printf ("show\n");
	vpos = vpos - text_size - 5;
	strcpy (title1, buf);
	title1_found = TRUE;
	set_text_font (text_size);
	if (toc_file && song_pages == 1)	/* generate index entry */
		{ /* careful to ignore any \ which may have been inserted */
		char *bp;
		for (bp = buf; *bp; bp++) if (*bp != '\\') putc(*bp, toc_file);
		fprintf(toc_file, " ... %d\n", page_label);
		}
	}
/* --------------------------------------------------------------------------------*/
do_subtitle()
	{
	char *buf;
	buf = (char *)strtok(NULL, "\0");
	use_text_font();
	printf ("(%s) dup stringwidth pop 2 div\n", buf);
	printf ("%d 2 div exch sub %d moveto\n", WIDTH , vpos);
	printf ("show\n");
	vpos = vpos - text_size ;
	}

/* --------------------------------------------------------------------------------*/
put_in_string(array, p_index, c, max_index, p_ov_flag)
unsigned char array[MAXLINE];
int *p_index;
unsigned char c;
int max_index;
int *p_ov_flag;
	{
	if (*p_index < max_index)
		{
		if ( (int) c < 128 )
			{
			array[(*p_index)++] = c;
			}
		else
			{
			array[(*p_index)++] = '\\';
			sprintf( &array[*p_index], "%o", c);
			(*p_index) += 3;
			}
		}
	else
		{
		if (!*p_ov_flag)
			{
			error ("Buffer Overflow");
			*p_ov_flag = TRUE;
			}
		}
	}
/* --------------------------------------------------------------------------------*/
process_file(source_fd)
FILE *source_fd;
	{
	debug("start of process_file");

	n_lines = 0;

	while ( (c=getc(source_fd)) != EOF )
		{
		i_input++;
		switch ((char)c) {

		case '[':
			if ( in_chord )
				error("Opening a chord within a chord!");
			else
				in_chord = TRUE;
			i_chord = 0;
			break;
		
		case ']':
			if ( in_chord )
				{
				in_chord = FALSE;
				chord[i_chord]='\0';
				do_chord(i_text, chord);
				has_chord = TRUE; 
				i_chord = 0;
				i_chord_ov = FALSE;
				}
			else
				error("']' found with no matching '['");
			break;
		
		case '{':
			in_directive = TRUE;
			i_directive = 0;
			has_directive = TRUE;
			break;

		case '}':
			if ( in_directive)
				{
				in_directive = FALSE;
				directive[i_directive]='\0';
				for (; (c=getc(source_fd)) != '\n'; );
				i_input = 0;
				do_directive(directive);
				has_directive = FALSE;
				n_lines++;
				i_directive = 0;
				i_directive_ov = FALSE;
				}
			else
				error("'}' found with no matching '{'");
			break;

		case '\n':
			if ( in_directive )
				error ("Line ends while in a directive !"); 
			if ( in_chord )
				error ("Line ends while in a chord !"); 
			if (has_directive == FALSE)
				{
				if (in_chordrc)
					error("line is NOT a directive");
				else
					{
					print_text_line();
					}
				}
			else
				has_directive = FALSE;
			n_lines++;
			i_input = 0;
			in_directive = FALSE;
			in_chord = FALSE;
			break;

		case '(':
		case ')':
			if (in_directive)
				{
				put_in_string(directive, &i_directive, '\\', MAXTOKEN, &i_directive_ov);
				put_in_string(directive, &i_directive, c, MAXTOKEN, &i_directive_ov);
				break;
				}
			else if (in_chord) /* allow parens in chord names */
				{
				put_in_string (chord, &i_chord, '\\', MAXLINE, &i_text_ov);
				put_in_string (chord, &i_chord, c, MAXLINE, &i_text_ov);
				break;
				}
			else
				{
				put_in_string (text_line, &i_text, '\\', MAXLINE, &i_text_ov);
				put_in_string (text_line, &i_text, c, MAXLINE, &i_text_ov);
				break;
				}
	
	/* This case HAS to be the last before the default statement !!! */

		case '#':
			if (i_input == 1)
				{
				for (; (c=getc(source_fd)) != '\n'; );
				n_lines++;
				i_input = 0;
				break;
				}

		default :
			if (in_chord )
				{
				if ( c != ' ' )
					put_in_string(chord, &i_chord, c, MAXTOKEN, &i_text_ov);
				}
			else if (in_directive)
				{
				put_in_string(directive, &i_directive, c, MAXTOKEN, &i_directive_ov);
				}
			else
				{
				put_in_string(text_line, &i_text, c, MAXLINE, &i_text_ov);
				}
			break;
			}
		}
	print_text_line();
	}

/* --------------------------------------------------------------------------------*/
/* read the file passed as argument */
read_input_file()
	{
	current_file = source;
	source_fd = fopen (source, "r");
	if (source_fd == NULL)
		{
		fprintf(stderr, "Unable to open [%s]\n", source);
		exit (1);
		}

	process_file(source_fd);
	fclose(source_fd);
	}

/* --------------------------------------------------------------------------------*/
/* read the file $HOME/.chordrc as a set of directive */
read_chordrc()
	{
	char chordrc[MAXTOKEN];
	FILE *chordrc_fd;
	int n_lines_save;

	strcpy (chordrc, getenv ("HOME"));
	strcat (chordrc,"/.chordrc\0");
	current_file = chordrc;
	chordrc_fd = fopen (chordrc, "r");
	if (chordrc_fd != NULL)
		{
		n_lines_save=n_lines;
		n_lines=1;
		in_chordrc = TRUE;
		process_file(chordrc_fd);
		in_chordrc = FALSE;
		n_lines=n_lines_save;
		fclose(chordrc_fd);
		}
	current_file = source;
	}

/* --------------------------------------------------------------------------------*/
debug(dbg_str)
char *dbg_str;
{
	if ( debug_mode )
		fprintf (stderr, "Debug: %s\n", dbg_str);
}

/* --------------------------------------------------------------------------------*/
print_version()
{
	char *version = VERSION;
	char *patch_level = PATCH_LEVEL;
	
	printf ("chord version %s, patchlevel %s\n", version, patch_level);
}
/* --------------------------------------------------------------------------------*/
error(err_str)
char *err_str;
{
	fprintf(stderr, "WARNING: %s\n",err_str);
	fprintf(stderr, "         in file %s, line %d\n", current_file,n_lines);
}

/* --------------------------------------------------------------------------------*/
main(argc, argv)
int argc;
char **argv;
	{
	int c,i;
/* Option Parsing */

	while ((c = getopt(argc, argv, "hlDd24Vc:t:g:C:T:x:aLRp:i:")) != -1)
		switch (c) {

		case 'h':
			do_help(argv[0]);
			exit(1);
			break;

		case 'D':
			debug_mode = TRUE;
			break;

		case 'd':
			init_known_chords();
			dump_chords();
			exit(0);

		case 'c':
			i = atoi (optarg);
			if ( i == 0 )
				error ("invalid value for chord_size");
			else
				chord_size = i;
			break;

		case 't':
			i = atoi (optarg);
			if ( i == 0 )
				error ("invalid value for text_size");
			else
				text_size = i;
			break;

		case 'x':
			i = atoi (optarg);
			if ( i == 0 )
				error ("invalid value for transposition");
			else
				transpose = i;
			break;

		case 'T':
			text_font = optarg;
			break;

		case 'C':
			chord_font = optarg;
			break;

		case 'g':
			i = atoi (optarg);
			if ( i == 0 )
				error ("invalid value for grid_size");
			else
				grid_size = i;
			break;

		case 'l':
			lyrics_only=TRUE;
			break;

		case 'V':
			print_version();
			exit(0);

		case '2':
			pagination=2;
			scale=scale2;
			rotation=90.0;
			break;

		case '4':
			pagination=4;
			scale=scale4;
			break;

		case 'i':
			if (!strcmp(optarg, "-")) toc_file = stderr;
			else toc_file = fopen(optarg, "w");
			if (toc_file == NULL) {
				perror (optarg);
				error ("unable to create index file");
			}
			break;

		case 'a':
			auto_space=TRUE;
			break;

		case 'p':
			page_label = atoi(optarg);
			number_all = TRUE;
			break;

		case 'L':
			left_foot_odd = 1;
			break;

		case 'R':
			left_foot_odd = 0;
			break;

		case '?':
			do_help(argv[0]);
			break;
		}


/* File Processing */

	init_ps();

	read_chordrc();

	chord_inc = chord_size * 1.5;

	if (optind  == argc)
		{
		debug ("Reading stdin");
		strcpy(source, "stdin");
		process_file (stdin);
		}
	else
		{
		for ( ; optind < argc; optind++ )
			{
			debug ("Processing a file");
			strcpy(source, argv[optind]);
			read_input_file();
			if (optind < argc - 1)
				do_new_song();
			}
		}


	do_end_of_song();
	if (v_pages != 0)
		{
		do_end_of_phys_page();
		}

	printf ("%%%%Trailer\n");
	printf ("%%%%Pages: %d 1\n", n_pages);
	printf ("%%%%EOF\n");
	if (toc_file) fclose(toc_file);
	exit (0);	
	return(0);
	}

