/*--------------------------------------------------------------------
 *	$Id: pslegend.c,v 1.93 2007/10/07 07:10:41 guru Exp $
 *
 *	Copyright (c) 1991-2007 by P. Wessel and W. H. F. Smith
 *	See COPYING file for copying and redistribution conditions.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; version 2 of the License.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	Contact info: gmt.soest.hawaii.edu
 *--------------------------------------------------------------------*/
/*
 * pslegend will make map legends from input that specifies what will go
 * into the legend, such as headers, symbols with explanatory text,
 * paragraph text, and empty space and horizontal/vertical lines.
 *
 * Author:	Paul Wessel
 * Date:	18-SEP-2001
 * Version:	4
 *
 */

#include "gmt.h"
#include "pslib.h"

#ifdef WIN32
#include <process.h>
#endif

struct PSLEGEND_CTRL {
	struct C {	/* -C<dx>/<dy> */
		BOOLEAN active;
		double dx, dy;
	} C;
	struct D {	/* -D[x]<x0>/<y0>/w/h/just */
		BOOLEAN active;
		BOOLEAN cartesian;
		double lon, lat, width, height;
		char justify[3];
	} D;
	struct F {	/* -F */
		BOOLEAN active;
	} F;
	struct G {	/* -G<fill> */
		BOOLEAN active;
		struct GMT_FILL fill;
	} G;
	struct L {	/* -L<spacing> */
		BOOLEAN active;
		double spacing;
	} L;
	struct S {	/* -C<script> */
		BOOLEAN active;
		char *file;
	} S;
};

int main (int argc, char **argv)
{
	BOOLEAN flush_paragraph = FALSE, draw_vertical_line = FALSE;
	
#ifdef WIN32
	char *del = "del", *null = "NUL", *escape = "^", quote = ' ';
#else
	char *del = "rm -f", *null = "/dev/null", *escape = "\\", quote = '\'';
#endif
	char txt_a[GMT_LONG_TEXT], txt_b[GMT_LONG_TEXT], txt_c[GMT_LONG_TEXT], txt_d[GMT_LONG_TEXT], txt_e[GMT_LONG_TEXT];
	char txt_f[GMT_LONG_TEXT], key[GMT_LONG_TEXT], *f = CNULL, sub[GMT_LONG_TEXT], cmd[BUFSIZ];
	char symbol[GMT_LONG_TEXT], text[BUFSIZ], *u = CNULL, image[BUFSIZ], xx[GMT_LONG_TEXT], yy[GMT_LONG_TEXT];
	char size[GMT_LONG_TEXT], angle[GMT_LONG_TEXT], mapscale[GMT_LONG_TEXT], font[GMT_LONG_TEXT], lspace[GMT_LONG_TEXT];
	char tw[GMT_LONG_TEXT], jj[GMT_LONG_TEXT], line[BUFSIZ], vpen[GMT_LONG_TEXT], script[GMT_LONG_TEXT];
	char sarg[GMT_LONG_TEXT], sparg[BUFSIZ], *jarg = CNULL, txtcolor[GMT_LONG_TEXT], psxy[GMT_LONG_TEXT], pstext[GMT_LONG_TEXT];
	char bar_cpt[GMT_LONG_TEXT], bar_gap[GMT_LONG_TEXT], bar_height[GMT_LONG_TEXT], bar_opts[BUFSIZ], mode;
	
	int i, k, n, justify = 0, n_columns = 1, error = 0, symbol_number = 0, n_files = 0, colon, n_scan;
	
	double x_off, west, east, south, north, x, y, x0, y0, L, off_ss, off_tt, V = 0.0;
	double half_line_spacing, quarter_line_spacing, one_line_spacing, y_start = 0.0, d_off, y_use;
	
	struct imageinfo header;
	struct PSLEGEND_CTRL *Ctrl;
	
	FILE *fp = NULL, *fpo;
	
	void *New_Pslegend_Ctrl (), Free_Pslegend_Ctrl (struct PSLEGEND_CTRL *C);
	
	/* Because pslegend uses system calls we must first make a copy of any arguments that GMT_begin will remove, such
	 * as +gmtdefaults and --PAR=value
	 */
	 
	memset ((void *)sparg, 0, BUFSIZ);
	for (i = 1, k = 0; i < argc; i++) {
		if (argv[i][0] == '+' || (argv[i][0] == '-' && argv[i][1] == '-')) {
			if (k) strcat (sparg, " ");
			strcat (sparg, argv[i]);
			k++;
		}
	}

	argc = GMT_begin (argc, argv);

	Ctrl = (struct PSLEGEND_CTRL *)New_Pslegend_Ctrl ();	/* Allocate and initialize a new control structure */

	/* Check and interpret the command line arguments */

	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-') {
			switch(argv[i][1]) {

				/* Common parameters */

				case 'J':
					jarg = argv[i];	/* Keep this for later */
				case 'K':
				case 'O':
				case 'P':
				case 'R':
				case 'V':
				case 'X':
				case 'x':
				case 'Y':
				case 'y':
				case 'c':
				case '\0':
					error += GMT_parse_common_options (argv[i], &west, &east, &south, &north);
					break;

				case 'U':	/* Just need a pointer to pass along */
					u = &argv[i][2];
					break;

				/* Supplemental parameters */

				case 'C':	/* Sets the clearance between frame and internal items */
					Ctrl->C.active = TRUE;
					sscanf (&argv[i][2], "%[^/]/%s", txt_a, txt_b);
					Ctrl->C.dx = GMT_convert_units (txt_a, GMT_INCH);
					Ctrl->C.dy = GMT_convert_units (txt_b, GMT_INCH);
					break;
				case 'D':	/* Sets position and size of legend */
					Ctrl->D.active = TRUE;
					if (argv[i][2] == 'x') {	/* Gave location directly in projected units (inches, cm, etc) */
						Ctrl->D.cartesian = TRUE;
						k = 3;
					}
					else				/* Gave lon, lat */
						k = 2;
					n = sscanf (&argv[i][k], "%[^/]/%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d, Ctrl->D.justify);
					if (n != 5) {
						fprintf (stderr, "%s ERROR: Syntax is -D[x]<xpos>/<ypos>/<width>/<height>/<justify>\n", GMT_program);
						error++;
					}
					if (argv[i][2] == 'x') {
						Ctrl->D.lon = GMT_convert_units (txt_a, GMT_INCH);
						Ctrl->D.lat = GMT_convert_units (txt_b, GMT_INCH);
					}
					else {	/* Given in user units, likely degrees */
						error += GMT_verify_expectations (GMT_io.in_col_type[0], GMT_scanf (txt_a, GMT_io.in_col_type[0], &Ctrl->D.lon), txt_a);
						error += GMT_verify_expectations (GMT_io.in_col_type[1], GMT_scanf (txt_b, GMT_io.in_col_type[1], &Ctrl->D.lat), txt_b);
					}
					Ctrl->D.width   = GMT_convert_units (txt_c, GMT_INCH);
					Ctrl->D.height  = GMT_convert_units (txt_d, GMT_INCH);
					break;
				case 'F':
					Ctrl->F.active = TRUE;
					break;
				case 'G':	/* Inside legend fill? */
					Ctrl->G.active = TRUE;
					if (GMT_getfill (&argv[i][2], &Ctrl->G.fill)) {	/* We check syntax here */
						GMT_fill_syntax ('G', " ");
						error++;
					}
					f = argv[i];		/* Pointer to fill argument */
					break;
				case 'L':			/* Sets linespacing in units of fontsize [1.1] */
					Ctrl->L.active = TRUE;
					Ctrl->L.spacing = atof (&argv[i][2]);
					break;
				case 'S':
					Ctrl->S.active = TRUE;
					if (argv[i][2]) {	/* Specified script filename */
						Ctrl->S.file = strdup (&argv[i][2]);
					}
					break;

				/* Options not recognized */

				default:
					error = TRUE;
					GMT_default_error (argv[i][1]);
					break;
			}
		}
		else if (n_files == 0) {
			if ((fp = GMT_fopen (argv[i], "r")) == NULL) {
				fprintf (stderr, "%s ERROR: Cannot open file %s\n", GMT_program, argv[i]);
				exit (EXIT_FAILURE);
			}
			n_files++;
		}
		else {
			fprintf (stderr, "%s ERROR: Only one file argument allowed\n", GMT_program);
			exit (EXIT_FAILURE);
		}
	}

	if (argc == 1 || GMT_give_synopsis_and_exit) {
		fprintf (stderr, "pslegend %s - To plot legends on maps\n\n", GMT_VERSION);
		fprintf (stderr, "usage: pslegend [<infofile>] -D[x]<x0>/<y0>/w/h/just %s %s\n", GMT_J_OPT, GMT_Rgeo_OPT);
		fprintf (stderr, "\t[-C<dx>/<dy>] [-F] [-G<fill>] [-K] [-L<spacing>] [-O] [-P] [-S[<script>]] [%s]\n", GMT_U_OPT);
		fprintf (stderr, "\t[-V] [%s] [%s] [%s] [%s]\n\n", GMT_U_OPT, GMT_X_OPT, GMT_Y_OPT, GMT_c_OPT);
		fprintf (stderr, "\tReads legend layout information from <infofile> [or stdin]\n");
		fprintf (stderr, "\t(See manual page for more information)\n");
		if (GMT_give_synopsis_and_exit) exit (EXIT_FAILURE);

		fprintf (stderr, "\t-D sets position and size of legend box.  Prepend x if coordinates are projected.\n");
		fprintf (stderr, "\t   Append the justification of the whole legend box using pstext justification codes.\n");
		GMT_explain_option ('j');
		GMT_explain_option ('R');
		fprintf (stderr, "\n\tOPTIONS:\n");
		GMT_explain_option ('b');
		fprintf (stderr, "\t-C sets the clearance between legend frame and internal items [0.05i/0.05i]\n");
		fprintf (stderr, "\t-F Draw border around the legend (using FRAME_PEN) [Default is no border]\n");
		GMT_fill_syntax ('G', "Set the fill for the legend box [Default is no fill].");
		GMT_explain_option ('K');
		fprintf (stderr, "\t-L Sets the linespacing factor in units of the current annotation font size [1.1]\n");
		GMT_explain_option ('O');
		GMT_explain_option ('P');
		fprintf (stderr, "\t-S Dump legend script to stdout, or optionally to file <script>.\n");
		fprintf (stderr, "\t   [Default is to write PostScript output]\n");
		GMT_explain_option ('U');
		GMT_explain_option ('V');
		GMT_explain_option ('X');
		GMT_explain_option ('.');
		exit (EXIT_FAILURE);
	}

	/* Check that the options selected are mutually consistent */

	if (Ctrl->C.dx < 0.0 || Ctrl->C.dy < 0.0) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR -C option:  clearances cannot be negative!\n", GMT_program);
		error++;
	}
	if (Ctrl->D.width < 0.0 || Ctrl->D.height < 0.0) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR -D option:  legend box sizes cannot be negative!\n", GMT_program);
		error++;
	}
	if (!project_info.region_supplied) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR:  Must specify -R option\n", GMT_program);
		error++;
	}

	if (error) exit (EXIT_FAILURE);

	if (Ctrl->S.active) {
		if (Ctrl->S.file) {
			if ((fpo = fopen (Ctrl->S.file, "w")) == NULL) {
				fprintf (stderr, "%s: GMT SYNTAX ERROR -S option:  Cannot create file %s\n", GMT_program, Ctrl->S.file);
				exit (EXIT_FAILURE);
			}
			if (gmtdefs.verbose) fprintf (stderr, "%s: Generate legend script %s\n", GMT_program, Ctrl->S.file);
		}
		else {
			fpo = GMT_stdout;
			if (gmtdefs.verbose) fprintf (stderr, "%s: Generate legend script [stdout]\n", GMT_program);
		}
	}
	else {
		if (gmtdefs.verbose) fprintf (stderr, "%s: Generate legend plot\n", GMT_program);
		sprintf (script, "GMT%d.bat", (int)getpid ());
		fpo = fopen (script, "w");
	}
#ifdef WIN32
	fprintf (fpo, "@ECHO OFF\n");
#endif
	if (!fp) fp = GMT_stdin;

	GMT_err_fail (GMT_map_setup (west, east, south, north), "");

	justify = GMT_just_decode (Ctrl->D.justify, -1, -1);
	
	if (!Ctrl->D.cartesian) {
		GMT_geo_to_xy (Ctrl->D.lon, Ctrl->D.lat, &x, &y);
		Ctrl->D.lon = x;	Ctrl->D.lat = y;
	}

	/* Adjust for -X -Y shifts */

	Ctrl->D.lon += GMT_ps.x_origin;
	Ctrl->D.lat += GMT_ps.y_origin;

	/* Allow for justification */

	Ctrl->D.lon -= 0.5 * ((justify-1)%4) * Ctrl->D.width;
	Ctrl->D.lat -= 0.5 * (justify/4) * Ctrl->D.height;

	/* Initialize psxy and pstext call strings with appropriate values */
	
	if (GMT_ps.absolute) {	/* Must pass the -Xa -Ya settings to every psxy and pstext call */
		mode = 'a';
		sprintf (pstext, "pstext -R -JX -O -K -X%c%gi -Y%c%gi %s", mode, Ctrl->D.lon, mode, Ctrl->D.lat, sparg);
		sprintf (psxy, "psxy -R -JX -O -K -X%c%gi -Y%c%gi %s", mode, Ctrl->D.lon, mode, Ctrl->D.lat, sparg);
	}
	else {			/* No need to pass, -X0 -Y0 are set implicitly */
		mode = 'r';
		sprintf (pstext, "pstext -R -JX -O -K %s", sparg);
		sprintf (psxy, "psxy -R -JX -O -K %s", sparg);
	}
	
	/* First draw legend frame box.  Note -JX%gi/-%gi which means y is positive down from the top of the box */

	if (Ctrl->F.active || Ctrl->G.active)
		sprintf (cmd, "psbasemap -R0/%g/0/%g -JX%gi/-%gi -X%c%gi -Y%c%gi -K %s", Ctrl->D.width, Ctrl->D.height, Ctrl->D.width, Ctrl->D.height, mode, Ctrl->D.lon, mode, Ctrl->D.lat, sparg);
	else
		sprintf (cmd, "psxy -R0/%g/0/%g -JX%gi/-%gi %s -X%c%gi -Y%c%gi -K %s", Ctrl->D.width, Ctrl->D.height, Ctrl->D.width, Ctrl->D.height, null, mode, Ctrl->D.lon, mode, Ctrl->D.lat, sparg);
	if (GMT_ps.portrait) strcat (cmd, " -P");
	if (Ctrl->F.active) strcat (cmd, " -B0");
	if (GMT_ps.overlay) strcat (cmd, " -O");
	if (Ctrl->G.active) {
		sprintf (sub, " %s", f);
		strcat (cmd, sub);
	}
	if (u) {
		sprintf (sub, " -U\"%s\"", u);
		strcat (cmd, sub);
	}
	fprintf (fpo, "%s\n", cmd);

	x0 = Ctrl->C.dx;
	y0 = Ctrl->C.dy;
	one_line_spacing = Ctrl->L.spacing * gmtdefs.annot_font_size[0] / 72.0;
	half_line_spacing = 0.5 * one_line_spacing;
	quarter_line_spacing = 0.25 * one_line_spacing;
	symbol_number = 0;

	while (GMT_fgets (line, BUFSIZ, fp)) {

		if (line[0] == '\0' || line[0] == '#') continue;	/* Skip blank lines or # comments */

		switch (line[0]) {
			case 'C':	/* Color change */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				sscanf (&line[2], "%[^\n]", txtcolor);
				if (GMT_ps.absolute) {	/* Must pass the -Xa -Ya settings to every psxy and pstext call */
					if (txtcolor[0] == '-') sprintf (pstext, "pstext -R -JX -O -K -X%c%gi -Y%c%gi %s", mode, Ctrl->D.lon, mode, Ctrl->D.lat, sparg);
					else sprintf (pstext, "pstext -R -JX -O -K -G%s -X%c%gi -Y%c%gi %s", txtcolor, mode, Ctrl->D.lon, mode, Ctrl->D.lat, sparg);
				}
				else {
					if (txtcolor[0] == '-') sprintf (pstext, "pstext -R -JX -O -K %s", sparg);
					else sprintf (pstext, "pstext -R -JX -O -K -G%s %s", txtcolor, sparg);
				}
				break;
			case 'B':	/* Color scale Bar */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				flush_paragraph = FALSE;
				if (n_columns > 1 && symbol_number > 0 && symbol_number%n_columns != 0) y0 += half_line_spacing, symbol_number = 0;	/* Did not end with full column */
				sscanf (&line[2], "%s %s %s %[^\n]", bar_cpt, bar_gap, bar_height, bar_opts);
				x_off = GMT_convert_units (bar_gap, GMT_INCH);
				L = GMT_convert_units (bar_height, GMT_INCH) + 2.0 * one_line_spacing;
				fprintf (fpo, "psscale -C%s -O -K -D%gi/%gi/%gi/%sh %s\n", bar_cpt, 0.5 * Ctrl->D.width, Ctrl->D.height-y0, Ctrl->D.width - 2 * x_off, bar_height, bar_opts);
				y0 += L + half_line_spacing;
				break;
			case 'H':	/* Header record */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				flush_paragraph = FALSE;
				if (n_columns > 1 && symbol_number > 0 && symbol_number%n_columns != 0) y0 += half_line_spacing, symbol_number = 0;	/* Did not end with full column */
				sscanf (&line[2], "%s %s %[^\n]", size, font, text);
				if (size[0] == '-') sprintf (size, "%g", gmtdefs.header_font_size);
				if (font[0] == '-') sprintf (font, "%d", gmtdefs.header_font);
				y0 += Ctrl->L.spacing * atoi (size) / 72.0;
				fprintf (fpo, "echo %c%g %g %s 0 %s CB %s%c | %s\n", quote, 0.5 * Ctrl->D.width, y0, size, font, text, quote, pstext);
				break;

			case 'I':	/* Image record */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				flush_paragraph = FALSE;
				if (n_columns > 1 && symbol_number > 0 && symbol_number%n_columns != 0) y0 += half_line_spacing, symbol_number = 0;	/* Did not end with full column */
				sscanf (&line[2], "%s %s %s", image, size, key);
				(void) ps_load_image (image, &header);
				justify = GMT_just_decode (key, -1, -1);
				x_off = (justify%4 == 1) ? x0 : ((justify%4 == 3) ? Ctrl->D.width - Ctrl->C.dx : 0.5 * Ctrl->D.width);
				L = GMT_convert_units (size, GMT_INCH) * ((double)header.height / (double)header.width);
				fprintf (fpo, "psimage -O -K %s -W%s -C%gi/%gi/%s\n", image, size, x_off, Ctrl->D.height-y0, key);
				y0 += L + half_line_spacing;
				break;

			case 'L':	/* Label record */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				flush_paragraph = FALSE;
				if (n_columns > 1 && symbol_number > 0 && symbol_number%n_columns != 0) y0 += half_line_spacing, symbol_number = 0;	/* Did not end with full column */
				sscanf (&line[2], "%s %s %s %[^\n]", size, font, key, text);
				if (size[0] == '-') sprintf (size, "%g", gmtdefs.label_font_size);
				if (font[0] == '-') sprintf (font, "%d", gmtdefs.label_font);
				y0 += Ctrl->L.spacing * atoi (size) / 72.0;
				justify = GMT_just_decode (key, -1, 0);
				x_off = (justify%4 == 1) ? x0 : ((justify%4 == 3) ? Ctrl->D.width - Ctrl->C.dx : 0.5 * Ctrl->D.width);
				fprintf (fpo, "echo %c%g %g %s 0 %s B%s %s%c | %s\n", quote, x_off, y0, size, font, key, text, quote, pstext);
				break;

			case 'M':	/* Map scale record M lon0|- lat0 length[:label:just] f|p [-R -J] */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				flush_paragraph = FALSE;
				if (n_columns > 1 && symbol_number > 0 && symbol_number%n_columns != 0) y0 += half_line_spacing, symbol_number = 0;	/* Did not end with full column */
				n_scan = sscanf (&line[2], "%s %s %s %s %s %s", txt_a, txt_b, txt_c, txt_d, txt_e, txt_f);
				k = (txt_d[0] != 'f') ? 1 : 0;	/* Determines if we start -L with f or not */
				for (i = colon = 0; txt_c[i] && colon != 2; i++) if (txt_c[i] == ':') colon++;
				if (colon) {	/* Specified alternate label (could be upper case, hence 0.85) and justification */
					d_off = 0.0;
					/* if (txt_c[i] == 't') d_off = 0.75 * (gmtdefs.map_scale_height + gmtdefs.label_font_size / 72.0); */
					if (txt_c[i] == 't') d_off = gmtdefs.label_offset + 0.85 * fabs (gmtdefs.label_font_size / 72.0);
				}
				else {	/* Default places a lower case title on top, hence only 0.75 size */
					/* d_off = 0.75 * (gmtdefs.map_scale_height + gmtdefs.label_font_size / 72.0); */
					d_off = gmtdefs.label_offset + 0.75 * fabs (gmtdefs.label_font_size / 72.0);
				}
				y0 += d_off - quarter_line_spacing;
				x_off = 0.5 * Ctrl->D.width;
				y_use = Ctrl->D.height - y0;
				if (!strcmp (txt_a, "-"))	/* No longitude needed */
					sprintf (mapscale, "fx%gi/%gi/%s/%s", 0.5 * Ctrl->D.width, y_use, txt_b, txt_c);
				else				/* Gave both lon and lat for scale */
					sprintf (mapscale, "fx%gi/%gi/%s/%s/%s", 0.5 * Ctrl->D.width, y_use, txt_a, txt_b, txt_c);

				if (n_scan == 6)	/* Gave specific -R -J on M line */
					fprintf (fpo, "psbasemap %s %s -O -K -L%s\n", txt_e, txt_f, &mapscale[k]);
				else	/* Use -R -J supplied to pslegend */
					fprintf (fpo, "psbasemap -R%g/%g/%g/%g %s -O -K -L%s\n", west, east, south, north, jarg, &mapscale[k]);
				/* Reset -R -J by calling a dummy psxy /dev/null */
				fprintf (fpo, "psxy -R0/%g/0/%g -JX%gi/-%gi -O -K %s\n", Ctrl->D.width, Ctrl->D.height, Ctrl->D.width, Ctrl->D.height, null);
				d_off = 0.0;
				if (colon && txt_c[i] == 'b') {	/* Specified bottom justification */
					d_off = 0.75 * gmtdefs.label_font_size / 72.0 + fabs(gmtdefs.label_offset);
				}
				else	/* Default */
					d_off = 0.0;
				/* y0 += half_line_spacing + gmtdefs.map_scale_height + d_off; */
				y0 += gmtdefs.map_scale_height + d_off + 0.75 * gmtdefs.annot_font_size[0] / 72.0 + quarter_line_spacing;
				break;

			case 'S':	/* Symbol record */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				flush_paragraph = FALSE;
				symbol_number++;
				sscanf (&line[2], "%s %s %s %s %s %s %[^\n]", txt_a, symbol, size, txt_c, txt_d, txt_b, text);
				off_ss = GMT_convert_units (txt_a, GMT_INCH);
				off_tt = GMT_convert_units (txt_b, GMT_INCH);
				if (n_columns == 1 || symbol_number%n_columns == 1) y0 += half_line_spacing;
				x_off = x0 + (Ctrl->D.width / n_columns) * ((symbol_number - 1) % n_columns);
				if (symbol[0] == 'f') {	/* Front is different, must plot as a line segment */
					i = 0;
					while (size[i] != '/' && size[i]) i++;
					if (size[i] != '/') {
						fprintf (stderr, "%s: ERROR: -Sf option must have a tick length\n", GMT_program);
						exit (EXIT_FAILURE);
					}
					size[i] = '\0';	/* Temporarily truncate */
					x = 0.5*GMT_convert_units (size, gmtdefs.measure_unit);
					size[i] = '/';	/* Undo truncation */
					i++;
					fprintf (fpo, "echo %g %g > pslegend_tmp.txt\necho %g %g >> pslegend_tmp.txt\n", x_off + off_ss-x, y0, x_off + off_ss+x, y0);
					sprintf (cmd, "%s -S%s%s pslegend_tmp.txt", psxy, symbol, &size[i]);
					if (txt_c[0] != '-') {
						sprintf (sub, " -G%s", txt_c);
						strcat (cmd, sub);
					}
					if (txt_d[0] != '-') {
						sprintf (sub, " -W%s", txt_d);
						strcat (cmd, sub);
					}
					fprintf (fpo, "%s\n", cmd);
					fprintf (fpo, "%s pslegend_tmp.txt\n", del);
				}
				else {	/* Regular symbols */
					if (symbol[0] == 'k')
						sprintf (sub, "%s/%s", symbol, size);
					else
						sprintf (sub, "%s%s", symbol, size);
					if (symbol[0] == 'E' || symbol[0] == 'e') {	/* Ellipse needs more arguments */
						x = 0.5*GMT_convert_units (size, gmtdefs.measure_unit);
						sprintf (sarg, "%g %g 0 %g %g", x_off + off_ss, y0, x, 0.65*x);
					}
					else if (symbol[0] == 'V' || symbol[0] == 'v') {	/* Vector also need more args */
						i = 0;
						while (size[i] != '/' && size[i]) i++;
						if (size[i] != '/') {	/* The necessary arguments not supplied! */
							sprintf (sub, "vb");
							exit (EXIT_FAILURE);
						}
						else {
							size[i++] = '\0';	/* So GMT_convert_units won'c complain */
							sprintf (sub, "%sb%s", symbol, &size[i]);
						}
						x = GMT_convert_units (size, gmtdefs.measure_unit);
						sprintf (sarg, "%g %g 0 %g", x_off + off_ss, y0, x);
					}
					else if (symbol[0] == 'w') {	/* Wedge also need more args */
						x = GMT_convert_units (size, GMT_INCH);
						sprintf (sarg, "%g %g 20 60", x_off + off_ss -0.5*x, y0+0.25*x);
					}
					else
						sprintf (sarg, "%g %g", x_off + off_ss, y0);
					sprintf (cmd, "echo %s | %s -S%s", sarg, psxy, sub);
					if (txt_c[0] != '-') {
						sprintf (sub, " -G%s", txt_c);
						strcat (cmd, sub);
					}
					if (txt_d[0] != '-') {
						sprintf (sub, " -W%s", txt_d);
						strcat (cmd, sub);
					}
					fprintf (fpo, "%s\n", cmd);
				}
				fprintf (fpo, "echo %c%g %g %g 0 %d LM %s%c | %s\n", quote, x_off + off_tt, y0, gmtdefs.annot_font_size[0], gmtdefs.annot_font[0], text, quote, pstext);
				if (n_columns == 1 || symbol_number%n_columns == 0) y0 += half_line_spacing;
				break;

			case 'D':	/* Delimiter record */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				flush_paragraph = FALSE;
				if (n_columns > 1 && symbol_number > 0 && symbol_number%n_columns != 0) y0 += half_line_spacing, symbol_number = 0;	/* Did not end with full column */
				sscanf (&line[2], "%s %s", txt_a, txt_b);
				L = GMT_convert_units (txt_a, GMT_INCH);
				y0 += quarter_line_spacing;
				fprintf (fpo, "echo %g %g > pslegend_tmp.txt\necho %g %g >> pslegend_tmp.txt\n", L, y0, Ctrl->D.width - L, y0);
				fprintf (fpo, "%s -W%s pslegend_tmp.txt\n%s pslegend_tmp.txt\n", psxy, txt_b, del);
				y0 += quarter_line_spacing;
				break;

			case 'G':	/* Gap record */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				flush_paragraph = FALSE;
				if (n_columns > 1 && symbol_number > 0 && symbol_number%n_columns != 0) y0 += half_line_spacing, symbol_number = 0;	/* Did not end with full column */
				sscanf (&line[2], "%s", txt_a);
				L = (txt_a[strlen(txt_a)-1] == 'l') ? atoi (txt_a) * one_line_spacing : GMT_convert_units (txt_a, GMT_INCH);
				y0 += L;
				break;
			case 'N':	/* n_columns record */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				flush_paragraph = FALSE;
				if (n_columns > 1 && symbol_number > 0 && symbol_number%n_columns != 0) y0 += half_line_spacing, symbol_number = 0;	/* Did not end with full column */
				sscanf (&line[2], "%s", txt_a);
				n_columns = atoi (txt_a);
				symbol_number = 0;
				break;

			case 'V':	/* Vertical line from here to next V */
				if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
				flush_paragraph = FALSE;
				if (n_columns > 1 && symbol_number > 0 && symbol_number%n_columns != 0) y0 += half_line_spacing, symbol_number = 0;	/* Did not end with full column */
				if (draw_vertical_line) {	/* Second time, now draw line */
					fprintf (fpo, "echo # vertical lines > pslegend_tmp.txt\n");
					for (i = 1; i < n_columns; i++) {
						x_off = i * Ctrl->D.width / n_columns;
						fprintf (fpo, "echo %s> bar %d >> pslegend_tmp.txt\n", escape, i);
						fprintf (fpo, "echo %g %g >> pslegend_tmp.txt\necho %g %g >> pslegend_tmp.txt\n", x_off, y_start+V-quarter_line_spacing, x_off, y0-V+quarter_line_spacing);
					}
					fprintf (fpo, "%s -W%s -H -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", psxy, vpen, del);
					draw_vertical_line = FALSE;
				}
				else {
					draw_vertical_line = TRUE;
					y_start = y0;
					sscanf (&line[2], "%s %s", txt_a, vpen);
					V = GMT_convert_units (txt_a, GMT_INCH);
				}
				break;

			case '>':	/* Paragraph text header */
				n = sscanf (&line[1], "%s %s %s %s %s %s %s %s %s", xx, yy, size, angle, font, key, lspace, tw, jj);
				if (!(n <= 0 || n == 9)) {
					fprintf (stderr, "%s: ERROR: The > record must have 0 or 9 arguments (only %d found)\n", GMT_program, n);
					exit (EXIT_FAILURE);
				}
				if (n < 0) n = 0;	/* Since -1 is returned if no arguments */
				if (n == 0 || xx[0] == '-') sprintf (xx, "%g", x0);
				if (n == 0 || yy[0] == '-') sprintf (yy, "%g", y0);
				if (n == 0 || size[0] == '-') sprintf (size, "%g", gmtdefs.annot_font_size[0]);
				if (n == 0 || angle[0] == '-') sprintf (angle, "0");
				if (n == 0 || font[0] == '-') sprintf (font, "%d", gmtdefs.annot_font[0]);
				if (n == 0 || key[0] == '-') sprintf (key, "TL");
				if (n == 0 || lspace[0] == '-') sprintf (lspace, "%gi", one_line_spacing);
				if (n == 0 || tw[0] == '-') sprintf (tw, "%gi", Ctrl->D.width - 2.0 * Ctrl->C.dx);
				if (n == 0 || jj[0] == '-') sprintf (jj, "j");
				fprintf (fpo, "echo %s> %s %s %s %s %s %s %s %s %s > pslegend_tmp.txt\n", escape, xx, yy, size, angle, font, key, lspace, tw, jj);
				flush_paragraph = TRUE;
				break;

			case 'T':	/* paragraph text record */
				sscanf (&line[2], "%[^\n]", text);
				fprintf (fpo, "echo %c%s%c >> pslegend_tmp.txt\n", quote, text, quote);
				break;

			default:
				fprintf (stderr, "%s: ERROR: Unrecognized record (%s)\n", GMT_program, line);
				exit (EXIT_FAILURE);
			break;
		}
	}
	if (fp != GMT_stdin) GMT_fclose (fp);

	if (flush_paragraph) fprintf (fpo, "%s -M pslegend_tmp.txt\n%s pslegend_tmp.txt\n", pstext, del);
	/* Revert to original region (-R), projection and size (-J) and position (-X, -Y) */
	sprintf (cmd, "psxy %s -R%g/%g/%g/%g %s -X%c%gi -Y%c%gi -O %s", null, west, east, south, north, jarg, mode, -Ctrl->D.lon+GMT_ps.x_origin, mode, -Ctrl->D.lat+GMT_ps.y_origin, sparg);
	if (!GMT_ps.last_page) strcat (cmd, " -K");
	fprintf (fpo, "%s\n", cmd);

	if (!Ctrl->S.active) {	/* Add auto-delete command at the end of the script and then execute it */
		fprintf (fpo, "%s %s\n", del, script);
		if (fpo != stdout) fclose (fpo);
		if (gmtdefs.verbose) fprintf (stderr, "%s: Executing legend script\n", GMT_program);
#ifdef WIN32
		system (script);
#else
		sprintf (cmd, "sh %s", script);
		system (cmd);
#endif
	}
	else if (fpo != stdout)
		fclose (fpo);

	if (gmtdefs.verbose) fprintf (stderr, "%s: Done\n", GMT_program);

	Free_Pslegend_Ctrl (Ctrl);	/* Deallocate control structure */

 	GMT_end (argc, argv);

	exit (EXIT_SUCCESS);
}

void *New_Pslegend_Ctrl () {	/* Allocate and initialize a new control structure */
	struct PSLEGEND_CTRL *C;
	
	C = (struct PSLEGEND_CTRL *) GMT_memory (VNULL, 1, sizeof (struct PSLEGEND_CTRL), "New_Pslegend_Ctrl");
	
	/* Initialize values whose defaults are not 0/FALSE/NULL */
	
	C->C.dx = C->C.dy = 0.05;	
	C->D.width = C->D.height = 1.0;
	C->L.spacing = 1.1;
	return ((void *)C);
}

void Free_Pslegend_Ctrl (struct PSLEGEND_CTRL *C) {	/* Deallocate control structure */
	if (C->S.file) free ((void *)C->S.file);	
	GMT_free ((void *)C);	
}
