/*--------------------------------------------------------------------
 *	$Id: psxy.c,v 1.69 2004/09/02 18:15:23 pwessel Exp $
 *
 *	Copyright (c) 1991-2004 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
 *--------------------------------------------------------------------*/
/*
 * psxy will read <x,y> pairs (or <lon,lat>) from GMT_stdin and plot
 * the lines, polygons, or symbols on a map. A variety of symbols
 * may be specified, some of which require additional columns of
 * data, like symbol-size etc.  Only one symbol may be plotted
 * at the time.  PostScript code is written to GMT_stdout.
 *
 * Author:	Paul Wessel
 * Date:	15-SEP-2001
 * Version:	4
 *
 */

#include "gmt.h"

#define NONE		99
#define UNSPECIFIED	999
#define LINE		0
#define BAR		1
#define CROSS		2
#define POINT		3
#define CIRCLE		4
#define SQUARE		5
#define TRIANGLE	6
#define DIAMOND		7
#define STAR		8
#define HEXAGON		9
#define ITRIANGLE	10
#define ELLIPSE		11
#define VECTOR		12	
#define VECTOR2		13
#define TEXT		14
#define PIE		15
#define XDASH		16
#define YDASH		17
#define RECT		18
#define PENTAGON	19
#define OCTAGON		20
#define CUSTOM		21
#define FRONT		-100
#define QUOTED_LINE	-200

/* FRONT symbols */

#define F_FAULT		0
#define F_TRIANGLE	1
#define F_SLIP		2
#define F_CIRCLE	3
#define F_BOX		4

/* Direction of front symbols: */

#define F_LEFT		+1
#define F_CENTERED	0
#define F_RIGHT		-1

#define POINTSIZE 0.005

struct FRONTLINE {		/* A sub-symbol for symbols along a front */
	double f_gap;		/* Gap between front symbols in inches */
	double f_len;		/* Length of front symbols in inches */
	double f_off;		/* Offset of first symbol from start of front in inches */
	int f_sense;		/* Draw symbols to left (+1), centered (0), or right (-1) of line */
	int f_symbol;		/* Which symbol to draw along the front line */
};

struct PSXY_SYMBOL {
	int symbol;		/* Symbol id */
	int n_required;		/* Number of additional columns necessary to decode chosen symbol */
	int u;			/* Measure unit id (0 = cm, 1 = inch, 2 = m, 3 = point */
	double size;		/* Current symbol size */
	double given_size;	/* Symbol size read from file or command line */
	BOOLEAN read_size;	/* TRUE when we must read symbol size from file */
	int font_no;		/* font to use for the -Sl symbol */

	/* These apply to bar symbols */

	double base;		/* From what level to draw the bar */
	BOOLEAN user_unit;	/* if TRUE */

	/* These apply to vectors */

	int convert_angles;	/* If 2, convert azimuth to angle on map, 1 special case for -JX, 0 plain case */
	BOOLEAN read_vector;	/* if TRUE must read vector attributes */
	BOOLEAN shrink;		/* If TRUE, shrink vector attributes for small lengths */
	double v_norm;		/* shrink when lengths are smaller than this */
	double v_shrink;	/* Required scale factor */
	double v_width;		/* Width of vector stem in inches */
	double h_length;	/* Length of vector head in inches */
	double h_width;		/* Width of vector head in inches */
	int v_just;		/* How to justify vector: head point given (3), head (2), center(1), tail (0 - Default) */
	BOOLEAN v_double_heads;		/* If TRUE, Add 8 (|= 8) to outline to specify double-headed vector (FALSE is single-headed) */

	char string[64];	/* Character code to plot (could be octal) */

	struct FRONTLINE f;	/* parameters needed for a front */
	struct CUSTOM_SYMBOL *custom;	/* pointer to a custom symbol */
	
	struct GMT_CONTOUR G;	/* For labelled lines */
} S;

double *xx, *yy, *xp, *yp;
int *plot_pen;

int decode_symbol_option (char *text, struct PSXY_SYMBOL *p);
void GMT_plot_ellipse (double lon, double lat, double major, double minor, double azimuth, struct GMT_FILL fill, int outline);
void GMT_draw_fence (double x[], double y[], int n, struct FRONTLINE *f, struct GMT_FILL *g, BOOLEAN outline);
void plot_x_errorbar (double x, double y, double delta_x, double error_width, double error_width2, int line);
void plot_y_errorbar (double x, double y, double delta_y, double error_width, double error_width2, int line);
void plot_x_whiskerbar (double x, double y, double hinge[], double error_width, double error_width2, int rgb[], int line);
void plot_y_whiskerbar (double x, double y, double hinge[], double error_width, double error_width2, int rgb[], int line);
void extract_label (char *line, char *label);

main (int argc, char **argv)
{

	int 	i, j, n, j0, n_files = 0, fno, xy_errors[2], two, three, four, error_type[2] = {0,0};
	int	n_alloc = GMT_CHUNK, plot_n, n_use, start, n_args, n_fields, n_cols_start = 2, this_outline;
	int 	n_required, n_total_read = 0, n_expected = 0, error_cols[2] = {1,4};
	
	BOOLEAN	error = FALSE, nofile = TRUE, polygon = FALSE, read_symbol = FALSE, penset_OK = TRUE;
	BOOLEAN	outline = FALSE, default_outline = FALSE, draw_arc = TRUE, error_bars = FALSE;
	BOOLEAN done, get_rgb = FALSE, greenwich, default_polygon = FALSE, resample, xy_offset = FALSE;
	BOOLEAN old_GMT_world_map, skip_if_outside = TRUE, close_polygon = FALSE, cpt_given = FALSE;
	BOOLEAN error_x = FALSE, error_y = FALSE, fill_set = FALSE, def_err_xy = FALSE, clip_set = FALSE;
	
	double west = 0.0, east = 0.0, south = 0.0, north = 0.0, s, c;
	double plot_x, plot_y, x_1, x_2, y_1, y_2, y0, error_width = 0.1, error_width2;
	double direction, length, major, minor, x2, y2, z, dir1, dir2, x_len, y_len, dx, dy;
	double v_w, h_l, h_w, tmp, step = 1.0, *in, font_size, x_offset, y_offset;
	
	char line[BUFSIZ], *cpt, *p, *symb_arg = CNULL, txt_a[32], txt_b[32];
	
	FILE *fp = NULL;
	
	struct GMT_PEN pen, epen, default_pen;
	struct GMT_FILL fill, default_fill, test_fill;
	
	xy_errors[0] = xy_errors[1] = 0;	/* These will be col # of where to find this info in data */
	
	argc = GMT_begin (argc, argv);
	
	GMT_init_pen (&pen, GMT_PENWIDTH);
	GMT_init_pen (&epen, GMT_PENWIDTH);
	GMT_init_fill (&fill, -1, -1, -1);	/* Default is no fill */
	GMT_contlabel_init (&S.G);
	
	/* Initialize PSXY_SYMBOL structure */

	S.symbol = S.n_required = S.convert_angles = 0;
	S.size = S.base = S.v_norm = S.v_shrink = 0.0;
	S.read_size = S.user_unit  = S.shrink = S.read_vector = FALSE;
	S.v_width = 0.03;
	memset ((void *)S.string, 0, (size_t)(16 * sizeof (char)));
	S.h_length = 0.12;
	S.h_width = 0.1;
	S.font_no = gmtdefs.annot_font[0];

	if ((S.u = gmtdefs.measure_unit) == GMT_CM) {
		S.v_width = 0.075 / 2.54;	S.h_length = 0.3 / 2.54;
		S.h_width = 0.25 / 2.54; error_width = 0.25 / 2.54;
	}
	
	/* Check and interpret the command line arguments */
	
	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-') {
			switch(argv[i][1]) {
		
				/* Common parameters */
			
				case 'B':
				case 'H':
				case 'J':
				case 'K':
				case 'O':
				case 'P':
				case 'R':
				case 'U':
				case 'V':
				case 'X':
				case 'x':
				case 'Y':
				case 'y':
				case 'b':
				case 'c':
				case 'f':
				case ':':
				case '\0':
					error += GMT_get_common_args (argv[i], &west, &east, &south, &north);
					break;
				
				/* Supplemental parameters */
			
				case 'A':	/* Turn off draw_arc mode */
					if (argv[i][2])
						step = atof (&argv[i][2]);	/* Undocumented test feature */
					else
						draw_arc = FALSE;
					break;
					
				case 'C':	/* Vary symbol color with z */
					cpt = &argv[i][2];
					cpt_given = TRUE;
					break;
				case 'D':
					if (sscanf (&argv[i][2], "%[^/]/%s", txt_a, txt_b) != 2) {
						fprintf (stderr, "%s: GMT SYNTAX ERROR -D option: Give x and y offsets\n", GMT_program);
						error++;
					}
					else {
						x_offset = GMT_convert_units (txt_a, GMT_INCH);
						y_offset = GMT_convert_units (txt_b, GMT_INCH);
						xy_offset = TRUE;
					}
					break;
				case 'E':	/* Get info for error bars and bow-whisker bars */
					for (j = 2; argv[i][j] && argv[i][j] != '/'; j++) {
						if (argv[i][j] == 'x') {
							xy_errors[0] = j;
							error_type[0] = 0;
						}
						else if (argv[i][j] == 'X') {	/* Box-whisker instead */
							xy_errors[0] = j;
							error_type[0] = 1;
						}
						else if (argv[i][j] == 'y') {
							xy_errors[1] = j;
							error_type[1] = 0;
						}
						else if (argv[i][j] == 'Y') {	/* Box-whisker instead */
							xy_errors[1] = j;
							error_type[1] = 1;
						}
						else {	/* Get error 'cap' width */
							strcpy (txt_a, &argv[i][j]);
							j0 = 0;
							while (txt_a[j0] && txt_a[j0] != '/') j0++;
							txt_a[j0] = 0;
							error_width = GMT_convert_units (txt_a, GMT_INCH);
							while (argv[i][j] && argv[i][j] != '/') j++;
							j--;
						}
					}
					error_bars = TRUE;
					if (!(xy_errors[0] || xy_errors[1])) {	/* Default is plain error bars for both */
						def_err_xy = TRUE;
						xy_errors[0] = 2;	/* Assumes xy input, later check for -: */
						xy_errors[1] = 3;
						error_type[0] = error_type[1] = 1;
					}
					if (argv[i][j] == '/') GMT_getpen (&argv[i][j+1], &epen);
					break;
				case 'G':		/* Set fill for symbols or polygon */
					if (!argv[i][2] || (argv[i][2] && GMT_getfill (&argv[i][2], &fill))) {
						GMT_fill_syntax ('G');
						error++;
					}
					fill_set = TRUE;
					break;
				case 'L':		/* Close line segments */
					close_polygon = TRUE;
					break;
				case 'M':		/* Multiple line segments */
					GMT_multisegment (&argv[i][2]);
					break;
				case 'N':		/* Do not skip points outside border */
					skip_if_outside = FALSE;
					break;
				case 'S':		/* Get symbol [and size] */
					symb_arg = &argv[i][2];
					error += decode_symbol_option (&argv[i][2], &S);
					break;
				case 'W':		/* Set line attributes */
					if (argv[i][2] && GMT_getpen (&argv[i][2], &pen)) {
						GMT_pen_syntax ('W');
						error++;
					}
					default_outline = outline = TRUE;
					break;
					
				/* Illegal options */
			
				default:		/* Options not recognized */
					error = TRUE;
					GMT_default_error (argv[i][1]);
					break;
			}
		}
		else
			n_files++;
	}
	
	if (argc == 1 || GMT_quick) {	/* Display usage */
		fprintf (stderr,"psxy %s - Plot lines, polygons, and symbols on maps\n\n", GMT_VERSION);
		fprintf (stderr,"usage: psxy <infiles> -J<params> -R<west/east/south/north> [-A] [-B<tickinfo>]\n");
		fprintf (stderr, "\t[-C<cpt>] [-D<dx>/<dy>] [-E[x|y|X|Y][cap][/<pen>]] [-G<fill>] [-H[<nrec>]] [-K] [-L]\n");
		fprintf (stderr, "\t[-M<flag>] [-N] [-O] [-P] [-S[<symbol>][<size>]] [-U[<label>]] [-V] [-W[<pen>]]\n");
		fprintf (stderr, "\t[-X<x_shift>] [-Y<y_shift>] [-c<ncopies>] [-:] [-bi[s][<n>]] [-f[i|o]<colinfo>]\n\n");
		
		if (GMT_quick) exit (EXIT_FAILURE);
		
		fprintf (stderr, "\t<infiles> is one or more files.  If no, read standard input\n");
		GMT_explain_option ('j');
		GMT_explain_option ('R');
		fprintf (stderr, "\n\tOPTIONS:\n");
		GMT_explain_option ('b');
		fprintf (stderr, "\t-A Suppress drawing line segments as great circle arcs\n");
		fprintf (stderr, "\t-C Use cpt-file to assign symbol colors based on z-value in 3rd column (requires -S)\n");
		fprintf (stderr, "\t   Without -S, psxy excepts -M lines/polygons and looks for -Z<val> options in each multiheader\n");
		fprintf (stderr, "\t-D Offset symbol positions by <dx>/<dy> [no offset].. Requires -S.\n");
		fprintf (stderr, "\t-E means draw error bars for x, y, or both.  Add cap-width [%g]. Append pen attributes.\n", error_width);
		fprintf (stderr, "\t   If X or Y is used instead a box-and-whisker diagram is drawn instead, using data from 4\n");
		fprintf (stderr, "\t   extra columns to get the 0 %%, 25 %%, 75 %% ,and 100%% quartiles (point value is assumed to be 50%%)\n");
		fprintf (stderr, "\t   Then, use -W, -G to control the appearance of the 25-75%% box.\n");
		fprintf (stderr, "\t-G Specify color (for symbols/polygons) or pattern (for polygons). Fill can be either\n");
		fprintf (stderr, "\t   1) <gray> (0-255) for gray-shade [0].\n");
		fprintf (stderr, "\t   2) <r/g/b> (each 0-255), <h/s/v> (0-360,0-1,0-1), or <c/m/y/k> (each 0-100) for color.\n");
		fprintf (stderr, "\t   3) black, white or [light|dark]{red, orange, yellow, green, cyan, blue, magenta, brown, gray}.\n");
		fprintf (stderr, "\t   4) p[or P]<dpi>/<pattern> for predefined patterns (1-90).\n");
		fprintf (stderr, "\t   5) not given; -G option is present in all subheader (requires -M and no -S)\n");
		fprintf (stderr, "\t   Default is no fill (transparent symbols or polygons)\n");
		GMT_explain_option ('H');
		GMT_explain_option ('K');
		fprintf (stderr, "\t-L Force closed polygons.\n");
		GMT_explain_option ('M');
		fprintf (stderr, "\t-N Do Not skip/clip symbols that fall outside map border [Default will ignore those outside]\n");
		GMT_explain_option ('O');
		GMT_explain_option ('P');
		fprintf (stderr, "\t-S to select symbol type and symbol size (in %s).  Choose between\n", GMT_unit_names[gmtdefs.measure_unit]);
		fprintf (stderr, "\t   -(xdash), st(a)r, (b)ar, (c)ircle, (d)iamond, (e)llipse, (f)ront, octa(g)on, (h)exagon, (i)nvtriangle,\n");
		fprintf (stderr, "\t   (k)ustom, (l)etter, pe(n)tagon, (p)oint, (q)uoted line, (r)ect, (s)quare, (t)riangle, (v)ector, (w)edge, (x)cross, (y)dash\n");
		fprintf (stderr, "\t   If no size is specified, psxy expects the 3rd column to have sizes.\n");
		fprintf (stderr, "\t   If no symbol is specified, psxy expects the last column to have symbol code.\n");
		fprintf (stderr, "\t   [Note: if -C is selected then 3rd means 4th column, etc.]\n");
		fprintf (stderr, "\t   Symbols A, C, D, G, H, I, N, S, T are adjusted to have same area as a circle of given size\n");
		fprintf (stderr, "\t   Bars: Append b<base> to give the y-value of the base [Default = 0]\n");
		fprintf (stderr, "\t      Append u if width is in x-input units [Default is %s]\n", GMT_unit_names[gmtdefs.measure_unit]);
		fprintf (stderr, "\t   Ellipses: the direction, major, and minor axis must be in input columns 3, 4 and 5.\n");
		fprintf (stderr, "\t     If -SE rather than -Se is selected, psxy will expect azimuth, and axes in km\n");
		fprintf (stderr, "\t     and convert azimuths based on the chosen map projection.  If projection is linear then\n");
		fprintf (stderr, "\t     we scale the minor/major axes by the map scale.\n");
		fprintf (stderr, "\t   Fronts: Give tickgap/ticklen[dir][type][:offset], where\n");
		fprintf (stderr, "\t     dir    = Plot symbol to the l(eft) or r(ight) of front [Default is centered]\n");
		fprintf (stderr, "\t     type   =  b(ox), c(ircle), f(ault), s(lip), t(riangle) [Default is f]\n");
		fprintf (stderr, "\t       box      : square when centered, half-square otherwise.\n"); 
		fprintf (stderr, "\t       circle   : full when centered, half-circle otherwise.\n"); 
		fprintf (stderr, "\t       fault    : centered cross-tick or tick only in the <dir> direction.\n"); 
		fprintf (stderr, "\t       slip     : left-lateral or right-lateral strike-slip arrows (centered is not defined).\n"); 
		fprintf (stderr, "\t       triangle : diagonal square when centered, directed triangle otherwise.\n"); 
		fprintf (stderr, "\t     offset = start plotting symbols when along-track distance equals offset [0].\n"); 
		fprintf (stderr, "\t   Kustom: append <symbolname> immediately after 'k'; this will look for <symbolname>.def in\n");
		fprintf (stderr, "\t     the current directory or in $GMTHOME/share/custom.\n");
		GMT_list_custom_symbols ();
		fprintf (stderr, "\t   Letter: append /<string> after symbol size, and optionally %%<font>\n");
		fprintf (stderr, "\t   Quoted line: Give [d|f|n|l|x]<info>[:<labelinfo>]\n");
		fprintf (stderr, "\t     <code><info> controls placement of labels along lines.  Choose among five algorithms:\n");
		GMT_cont_syntax (7, 1);
		fprintf (stderr, "\t     <labelinfo> controls the specifics of the labels.  Append what you need:\n");
		GMT_label_syntax (7, 1);
		fprintf (stderr, "\t   Rectangles: the x- and y-dimension must be in input columns 3 and 4.\n");
		fprintf (stderr, "\t   Vectors: the direction and length must be in input columns 3 and 4.\n");
		fprintf (stderr, "\t     Furthermore, <size> means arrowwidth/headlength/headwith [Default is %g/%g/%g]\n", S.v_width, S.h_length, S.h_width);
		fprintf (stderr, "\t     If -SV rather than -Sv is selected, psxy will expect azimuth and length\n");
		fprintf (stderr, "\t     and convert azimuths based on the chosen map projection\n");
		fprintf (stderr, "\t     Insert h(head), b(balance point), or t(ail) after -Sv|V to justify vector w.r.t. input (x,y).\n");
		fprintf (stderr, "\t     Insert s(egment) if (x,y) is tail and columns 3 and 4 holds head (x,y).\n");
		fprintf (stderr, "\t     Upper case H, B, T, S will give double-headed vector [Default is t]\n");
		fprintf (stderr, "\t   Wedges: the start and stop directions of pie wedge must be in input columns 3, 4.\n");
		GMT_explain_option ('U');
		GMT_explain_option ('V');
		fprintf (stderr, "\t-W sets pen attributes [width = %gp, color = black, texture = solid line].\n", pen.width);
		fprintf (stderr, "\t   Implicitly draws symbol outline with this pen.\n");
		fprintf (stderr, "\t   OR -W:<width>:<color>:<texture>: (leave empty to obtain the default values [:thinnest:black::]\n");
		fprintf (stderr, "\t   <width> = faint or {thin, thick, fat}[er|est].\n");
		fprintf (stderr, "\t   <color> = black, white or [light|dark]{red, orange, yellow, green, cyan, blue, magenta, brown, gray}.\n");
		fprintf (stderr, "\t   <texture> = pattern of dashes (-) and dots (.) which will be scaled by pen width.\n");
		GMT_explain_option ('X');
		GMT_explain_option ('c');
		GMT_explain_option (':');
		GMT_explain_option ('i');
		GMT_explain_option ('n');
		fprintf (stderr, "\t   Default is the required number of columns\n");
		GMT_explain_option ('f');
		GMT_explain_option ('.');
		exit (EXIT_FAILURE);
	}

	/* Check that the options selected are mutually consistent */
	
	if (S.symbol > 0 && cpt_given) get_rgb = TRUE;	/* Need to read z-vales from input data file */
	
	if (error_bars && (S.read_vector || S.symbol == ELLIPSE || S.symbol == FRONT || S.symbol == QUOTED_LINE)) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR -E option: Incompatible with -Se, -Sf, -Sq, -Sv\n", GMT_program);
		error++;
	}
	if (fill.use_pattern && !(S.symbol == LINE || S.symbol == CUSTOM)) {	/* fill-pattern only for polygons */
		error++;
		fprintf (stderr, "%s: GMT SYNTAX ERROR -G option: Fill-pattern only used with polygons\n", GMT_program);
	}
	if (cpt_given && S.symbol == 0 && !GMT_io.multi_segments) {
		error++;
		fprintf (stderr, "%s: GMT SYNTAX ERROR -C option: Must also specify a symbol (see -S) or give polygon file with -M\n", GMT_program);
	}
	if (!project_info.region_supplied) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR:  Must specify -R option\n", GMT_program);
		error++;
	}
	if (GMT_io.binary[GMT_IN] && gmtdefs.io_header[GMT_IN]) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR.  Binary input data cannot have header -H\n", GMT_program);
		error++;
	}
	if (GMT_io.binary[GMT_IN] && S.symbol == UNSPECIFIED) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR.  Binary input data cannot have symbol information\n", GMT_program);
		error++;
	}
	if (S.symbol == 0 && xy_offset) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR.  -D option only applies to symbols, not lines\n", GMT_program);
		error++;
	}

	if (error) exit (EXIT_FAILURE);

	read_symbol = (S.symbol == UNSPECIFIED);	/* Only for ASCII input */

	if (S.symbol == 0 && (fill_set || close_polygon)) polygon = TRUE;
	if (S.symbol == FRONT || S.symbol == QUOTED_LINE) polygon = FALSE;
	
	if (S.symbol && !(fill_set || cpt_given)) outline = TRUE;	/* User wants symbol but no fill - implicitly set outline */
	
	default_pen = pen;
	default_fill = fill;
	default_outline = outline;
	default_polygon = polygon;

	if (error_bars && S.symbol == 0)	/* Assume user only wants error bars */
		S.symbol = NONE;
		
	if (cpt_given) {
		GMT_read_cpt (cpt);
		if (get_rgb) n_cols_start++;
	}
		
	if (n_files > 0)
		nofile = FALSE;
	else
		n_files = 1;
	n_args = (argc > 1) ? argc : 2;

	if (error_bars) {
		if (xy_errors[0] && xy_errors[1] && error_type[0] == 1) xy_errors[1] += 3;	/* Need 4 columns for whisker bars */
		if (def_err_xy && gmtdefs.xy_toggle[0]) {	/* -E should be -Eyx */
			i_swap (xy_errors[0], xy_errors[1]);
			i_swap (error_type[0], error_type[1]);
		}
		if (xy_errors[0]) n_cols_start += error_cols[error_type[0]], error_x = TRUE;
		if (xy_errors[1]) n_cols_start += error_cols[error_type[1]], error_y = TRUE;
		xy_errors[0] += (S.read_size + get_rgb);	/* Move 0-2 columns over */
		xy_errors[1] += (S.read_size + get_rgb);
		error_width2 = 0.5 * error_width;
	}
	two   = (get_rgb) ? 3 : 2;
	three = (get_rgb) ? 4 : 3;
	four  = (get_rgb) ? 5 : 4;
	n_required = n_cols_start + S.n_required;

	if (GMT_io.binary[GMT_IN] && GMT_io.ncol[GMT_IN] == 0) GMT_io.ncol[GMT_IN] = n_required;
	n_expected = (GMT_io.binary[GMT_IN]) ? GMT_io.ncol[GMT_IN] : n_required;
	if (n_expected < n_required) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR.  Binary input data must have at least %d columns\n", GMT_program, n_required);
		error++;
	}

	GMT_put_history (argc, argv);	/* Update .gmtcommands4 */

	if (GMT_io.binary[GMT_IN] && gmtdefs.verbose) {
		char *type[2] = {"double", "single"};
		fprintf (stderr, "%s: Expects %d-column %s-precision binary data\n", GMT_program, GMT_io.ncol[GMT_IN], type[GMT_io.single_precision[GMT_IN]]);
	}

	GMT_map_setup (west, east, south, north);

	if (S.symbol == QUOTED_LINE) {
		if (GMT_contlabel_prep (&S.G, NULL, 0)) exit (EXIT_FAILURE);
		penset_OK = FALSE;	/* Since it is set in PSL */
	}

	resample = (draw_arc && MAPPING);
	greenwich = (project_info.w <= 0.0 || project_info.e <= 0.0);
	
	ps_plotinit (CNULL, gmtdefs.overlay, gmtdefs.page_orientation, gmtdefs.x_origin, gmtdefs.y_origin,
		gmtdefs.global_x_scale, gmtdefs.global_y_scale, gmtdefs.n_copies,
		gmtdefs.dpi, GMT_INCH, gmtdefs.paper_width, gmtdefs.page_rgb, gmtdefs.encoding.name, GMT_epsinfo (argv[0]));
	GMT_echo_command (argc, argv);
	if (gmtdefs.unix_time) GMT_timestamp (argc, argv);
	
	if (penset_OK) GMT_setpen (&pen);
	if (S.symbol == TEXT && fill_set && !outline) ps_setpaint (fill.rgb);
	
	if (S.symbol == TEXT) {
		ps_setfont (S.font_no);			/* Set the required font */
	}
	if (S.symbol == FRONT || S.symbol == QUOTED_LINE || (S.symbol > 0 && skip_if_outside)) {
		GMT_map_clip_on (GMT_no_rgb, 3);
		clip_set = TRUE;
	}
	if (S.symbol == ELLIPSE) skip_if_outside = FALSE;	/* So we can see ellipses that have centers outside -R */
	
	old_GMT_world_map = GMT_world_map;
	if (S.convert_angles && MAPPING) S.convert_angles = 2;
	
	if (S.symbol <= 0) {
		xx = (double *) GMT_memory (VNULL, (size_t)n_alloc, sizeof (double), GMT_program);
		yy = (double *) GMT_memory (VNULL, (size_t)n_alloc, sizeof (double), GMT_program);
	}
	done = FALSE;
	for (fno = 1; !done && fno < n_args; fno++) {	/* Loop over all input files */
		if (!nofile && argv[fno][0] == '-') continue;
		if (nofile) {
			fp = GMT_stdin;
			done = TRUE;
#ifdef SET_IO_MODE
			GMT_setmode (GMT_IN);
#endif
		}
		else if ((fp = GMT_fopen (argv[fno], GMT_io.r_mode)) == NULL) {
			fprintf (stderr, "%s: Cannot open file %s\n", GMT_program, argv[fno]);
			continue;
		}

		GMT_io.file_no++;
		GMT_io.seg_no = -1;
		if (!nofile && gmtdefs.verbose) {
			fprintf (stderr, "%s: Working on file %s\n", GMT_program, argv[fno]);
			sprintf (line, "File: %s", argv[fno]);
			ps_comment (line);
		}
		if (gmtdefs.io_header[GMT_IN]) {
			for (i = 0; i < gmtdefs.n_header_recs; i++) {
				GMT_fgets (line, BUFSIZ, fp);
				if (i == 0 && S.G.label_type == 2) extract_label (&line[1], S.G.label);	/* Set initial header as potential label */
			}
		}		
		if (S.symbol > 0) {	/* symbol part (not counting FRONT and QUOTED_LINE) */
		
			GMT_world_map = (S.symbol == ELLIPSE && S.convert_angles) ? FALSE : TRUE;
			if (read_symbol) n_expected = BUFSIZ;
			while ((n_fields = GMT_input (fp, &n_expected, &in)) >= 0 && !(GMT_io.status & GMT_IO_EOF)) {

				while ((GMT_io.status & GMT_IO_SEGMENT_HEADER) && !(GMT_io.status & GMT_IO_EOF)) {	/* Skip segment headers */
					if (gmtdefs.verbose) ps_comment (GMT_io.segment_header);
					if ((p = strstr (GMT_io.segment_header, " -G")) || (p = strstr (GMT_io.segment_header, "	-G"))) {
						strcpy (line, &p[3]);
						for (i = 0; line[i]; i++) if (line[i] == ' ') line[i] = '\0';
						if (!GMT_getfill (line, &test_fill)) fill = test_fill;	/* Failure is OK since -G may appear in text strings */
					}
					else if (cpt_given && ((p = strstr (GMT_io.segment_header, " -Z")) || (p = strstr (GMT_io.segment_header, "	-Z")))) {	/* Set symbol r/g/b via cpt-lookup */
						if(!strncmp (&p[3], "NaN", 3))	/* Got -ZNaN */
							GMT_get_rgb24 (GMT_d_NaN, fill.rgb);
						else {
							if (sscanf (&p[3], "%lg", &z) == 1) GMT_get_rgb24 (z, fill.rgb);
						}
					}
					else
						fill = default_fill;
					if ((p = strstr (GMT_io.segment_header, " -W")) || (p = strstr (GMT_io.segment_header, "	-W"))) {
						strcpy (line, &p[3]);
						for (i = 0; line[i]; i++) if (line[i] == ' ') line[i] = '\0';
						if (!GMT_getpen (line, &pen)) {	/* Failure is OK since -W may appear in text strings */
							GMT_setpen (&pen);
							outline = TRUE;
						}
					}
					else {
						GMT_setpen (&default_pen);
						outline = default_outline;
					}
					if (read_symbol) n_expected = BUFSIZ;
					n_fields = GMT_input (fp, &n_expected, &in);
				}
				if ((GMT_io.status & GMT_IO_EOF)) continue;	/* At EOF */

				if (GMT_io.status & GMT_IO_MISMATCH && !read_symbol) {
					fprintf (stderr, "%s: Mismatch between actual (%d) and expected (%d) fields near line %d\n", GMT_program, n_fields, n_expected, n_total_read);
					exit (EXIT_FAILURE);
				}
				n_total_read++;
			
				if (S.read_size) {
					S.size = in[two] * GMT_u2u[S.u][GMT_INCH];
				}
				if (read_symbol) {
					i = strlen (GMT_io.current_record) - 1;
					GMT_io.current_record[i--] = 0;	/* Chop off the \n */
					while (GMT_io.current_record[i] && !strchr (" ,\t", (int)GMT_io.current_record[i])) i--;
					(void ) decode_symbol_option (&GMT_io.current_record[i+1], &S);
					n_required = n_cols_start + S.n_required;
				}
			/*	else {
					(void ) decode_symbol_option (symb_arg, &S);
					n_required = n_cols_start + S.n_required;
				} */

				if (S.symbol == BAR) GMT_geo_to_xy (west, S.base, &plot_x, &y0);	/* Zero level for bars */
				if (get_rgb) {
					GMT_get_rgb24 (in[2], fill.rgb);
					if (S.symbol == CROSS || S.symbol == POINT || S.symbol == TEXT) ps_setpaint (fill.rgb);
				}
				if (GMT_cpt_skip) continue;	/* Chosen cpt file indicates skip for this z */
				
				if (S.read_vector) {
					direction = in[two];
					length = in[three] * GMT_u2u[S.u][GMT_INCH];
				}
				else if (S.symbol == ELLIPSE && S.convert_angles == 2) {	/* Leave axes in km */
					direction = in[two];
					major = in[three];
					minor = in[four];
				}
				else if (S.symbol == ELLIPSE && S.convert_angles) {	/* Scale lengths by given scale */
					direction = in[two];
					major = in[three] * project_info.x_scale;
					minor = in[four] * project_info.x_scale;
				}
				else if (S.symbol == ELLIPSE) {
					direction = in[two];
					major = in[three] * GMT_u2u[S.u][GMT_INCH];
					minor = in[four] * GMT_u2u[S.u][GMT_INCH];
				}
				else if (S.symbol == PIE) {
					dir1 = in[two+S.read_size];
					dir2 = in[three+S.read_size];
				}
				else if (S.symbol == RECT) {
					x_len = 0.5 * in[two+S.read_size];
					y_len = 0.5 * in[three+S.read_size];
				}
				
				/* Skip zero-size symbols */
				
				if (!(S.symbol == POINT || S.symbol == BAR) && S.symbol < ELLIPSE && S.size <= 0.0) {
					if (gmtdefs.verbose) fprintf (stderr, "%s: Warning: Symbol size <= 0.0 near line %d (skipped)\n", GMT_program, n_total_read);
					continue;
				}
				
				if (S.read_vector && S.v_just < 3 && length <= 0.0) {
					if (gmtdefs.verbose) fprintf (stderr, "%s: Warning: Vector length <= 0.0 near line %d (skipped)\n", GMT_program, n_total_read);
					continue;
				}
				if (S.symbol == ELLIPSE && (major <= 0.0 || minor <= 0.0)) {
					if (gmtdefs.verbose) fprintf (stderr, "%s: Warning: Ellipse axes <= 0.0 near line %d (skipped)\n", GMT_program, n_total_read);
					continue;
				}

				if (skip_if_outside) {
					GMT_map_outside (in[0], in[1]);
					if ( abs (GMT_x_status_new) > 1 || abs (GMT_y_status_new) > 1) continue;
				}

				GMT_geo_to_xy (in[0], in[1], &plot_x, &plot_y);
				if (GMT_is_dnan (plot_x)) {
					fprintf (stderr, "%s: Data point with x = NaN near line %d\n", GMT_program, n_total_read);
					continue;
				}
				if (GMT_is_dnan (plot_y)) {
					if (gmtdefs.verbose) fprintf (stderr, "%s: Warning: Data point with y = NaN near line %d\n", GMT_program, n_total_read);
					continue;
				}
				if (xy_offset) {
					plot_x += x_offset;
					plot_y += y_offset;
				}
				if (error_bars) {
					GMT_setpen (&epen);
					if (error_x) {
						if (error_type[0] == 0)
							plot_x_errorbar (in[0], in[1], in[xy_errors[0]], error_width, error_width2, n_total_read);
						else
							plot_x_whiskerbar (plot_x, in[1], &in[xy_errors[0]], error_width, error_width2, fill.rgb, n_total_read);
					}
					if (error_y) {
						if (error_type[1] == 0)
							plot_y_errorbar (in[0], in[1], in[xy_errors[1]], error_width, error_width2, n_total_read);
						else
							plot_y_whiskerbar (in[0], plot_y, &in[xy_errors[1]], error_width, error_width2, fill.rgb, n_total_read);
					}
					GMT_setpen (&pen);
				}

				switch (S.symbol) {
					case NONE:
						break;
					case STAR:
						ps_star (plot_x, plot_y, S.size, fill.rgb, outline);
						break;
					case BAR:
						if (S.user_unit) {	/* Width measured in x units */
							GMT_geo_to_xy (in[0]-S.size, S.base, &x_1, &y_1);
							GMT_geo_to_xy (in[0]+S.size, in[1], &x_2, &y_2);
					
							ps_rect (x_1, y_1, x_2, y_2, fill.rgb, outline);
						}
						else
							ps_rect (plot_x-S.size, y0, plot_x+S.size, plot_y, fill.rgb, outline);
						break;
					case CROSS:
						ps_cross (plot_x, plot_y, S.size);
						break;
					case POINT:
						ps_cross (plot_x, plot_y, POINTSIZE);
						break;
					case CIRCLE:
						ps_circle (plot_x, plot_y, S.size, fill.rgb, outline);
						break;
					case SQUARE:
						ps_square (plot_x, plot_y, S.size, fill.rgb, outline);
						break;
					case HEXAGON:
						ps_hexagon (plot_x, plot_y, S.size, fill.rgb, outline);
						break;
					case PENTAGON:
						ps_pentagon (plot_x, plot_y, S.size, fill.rgb, outline);
						break;
					case OCTAGON:
						ps_octagon (plot_x, plot_y, S.size, fill.rgb, outline);
						break;
					case TRIANGLE:
						ps_triangle (plot_x, plot_y, S.size, fill.rgb, outline);
						break;
					case ITRIANGLE:
						ps_itriangle (plot_x, plot_y, S.size, fill.rgb, outline);
						break;
					case DIAMOND:
						ps_diamond (plot_x, plot_y, S.size, fill.rgb, outline);
						break;
					case TEXT:
						font_size = S.size * 72.0;
						if (outline && fill_set) {
							ps_setpaint (fill.rgb);
							ps_text (plot_x, plot_y, font_size, S.string, 0.0, 6, FALSE);
							ps_setpaint (pen.rgb);
							ps_text (plot_x, plot_y, font_size, S.string, 0.0, 6, TRUE);
						}
						else if (fill_set)
							ps_text (plot_x, plot_y, font_size, S.string, 0.0, 6, FALSE);
						else
							ps_text (plot_x, plot_y, font_size, S.string, 0.0, 6, TRUE);
						break;
					case ELLIPSE:
						if (S.convert_angles == 2)
							GMT_plot_ellipse (in[0], in[1], major, minor, direction, fill, outline);
						else
							ps_ellipse (plot_x, plot_y, direction, major, minor, fill.rgb, outline);
						break;
					case VECTOR:
						if (S.convert_angles == 2) {
							GMT_azim_to_angle (in[0], in[1], 0.1, direction, &tmp);
							direction = tmp;
						}
						if (S.v_just == 3) {
							GMT_geo_to_xy (in[2], in[3], &x2, &y2);
							if (GMT_is_dnan (x2) || GMT_is_dnan (y2)) {
								fprintf (stderr, "%s: Warning: Vector head coordinates contain NaNs near line %d. Skipped\n", GMT_program, n_total_read);
								continue;
							}
						}
						else {
							sincos (direction * D2R, &s, &c);
							x2 = plot_x + length * c;
							y2 = plot_y + length * s;
							if (S.v_just) {
								dx = S.v_just * 0.5 * (x2 - plot_x);	dy = S.v_just * 0.5 * (y2 - plot_y);
								plot_x -= dx;		plot_y -= dy;
								x2 -= dx;		y2 -= dy;
							}
						}
						this_outline = (S.v_double_heads) ? outline + 8 : outline;
						ps_vector (plot_x, plot_y, x2, y2, S.v_width, S.h_length, S.h_width, gmtdefs.vector_shape, fill.rgb, this_outline);
						break;
					case VECTOR2:
						if (S.convert_angles == 2) {
							GMT_azim_to_angle (in[0], in[1], 1.0, direction, &tmp);
							direction = tmp;
						}
						if (S.v_just == 3) {
							GMT_geo_to_xy (in[2], in[3], &x2, &y2);
							if (GMT_is_dnan (x2) || GMT_is_dnan (y2)) {
								fprintf (stderr, "%s: Warning: Vector head coordinates contain NaNs near line %d. Skipped\n", GMT_program, n_total_read);
								continue;
							}
						}
						else {
							sincos (direction * D2R, &s, &c);
							x2 = plot_x + length * c;
							y2 = plot_y + length * s;
							if (S.v_just) {
								dx = S.v_just * 0.5 * (x2 - plot_x);	dy = S.v_just * 0.5 * (y2 - plot_y);
								plot_x -= dx;		plot_y -= dy;
								x2 -= dx;		y2 -= dy;
							}
						}
						this_outline = (S.v_double_heads) ? outline + 8 : outline;
						if (length < S.v_norm) {	/* Scale arrow attributes down with length */
							v_w = S.v_width * length * S.v_shrink;
							h_l = S.h_length * length * S.v_shrink;
							h_w = S.h_width * length * S.v_shrink;
							ps_vector (plot_x, plot_y, x2, y2, v_w, h_l, h_w, gmtdefs.vector_shape, fill.rgb, this_outline);
						}
						else	/* Leave as specified */
							ps_vector (plot_x, plot_y, x2, y2, S.v_width, S.h_length, S.h_width, gmtdefs.vector_shape, fill.rgb, this_outline);
						break;
					case PIE:
						ps_pie (plot_x, plot_y, S.size, dir1, dir2, fill.rgb, outline);
						break;
					case XDASH:
						ps_segment (plot_x - S.size, plot_y, plot_x + S.size, plot_y);
						break;
					case YDASH:
						ps_segment (plot_x, plot_y - S.size, plot_x, plot_y + S.size);
						break;
					case RECT:
						ps_rect (plot_x - x_len, plot_y - y_len, plot_x + x_len, plot_y + y_len, fill.rgb, outline);
						break;
					case CUSTOM:
						GMT_draw_custom_symbol (plot_x, plot_y, 0.0, S.size, S.custom, &pen, &fill, outline);
						break;
				}
				if (read_symbol) n_expected = BUFSIZ;
			}
		}
		else {	/* Line/polygon part */

			n_required = 2;
			n_fields = GMT_input (fp, &n_expected, &in);
			while (! (GMT_io.status & GMT_IO_EOF)) {	/* Not yet EOF */

				while (GMT_io.status & GMT_IO_SEGMENT_HEADER) {
					if (gmtdefs.verbose) ps_comment (GMT_io.segment_header);
					if ((p = strstr (GMT_io.segment_header, " -W")) || (p = strstr (GMT_io.segment_header, "	-W"))) {
						strcpy (line, &p[3]);
						for (i = 0; line[i]; i++) if (line[i] == ' ') line[i] = '\0';
						if (!GMT_getpen (line, &pen)) {	/* Failure is OK since -W may appear in text strings */
							if (penset_OK) GMT_setpen (&pen);
							outline = TRUE;
						}
					}
					else {
						if (penset_OK) GMT_setpen (&default_pen);
						outline = default_outline;
						pen = default_pen;
					}
					if ((p = strstr (GMT_io.segment_header, " -G")) || (p = strstr (GMT_io.segment_header, "	-G"))) {	/* Set polygon fill */
						strcpy (line, &p[3]);
						for (i = 0; line[i]; i++) if (line[i] == ' ') line[i] = '\0';
						if (!GMT_getfill (line, &test_fill)) {	/* Failure is OK since -G may appear in text strings */
							fill = test_fill;
							polygon = TRUE;
						}
					}
					else if (cpt_given && ((p = strstr (GMT_io.segment_header, " -Z")) || (p = strstr (GMT_io.segment_header, "	-Z")))) {	/* Set polygon or pen r/g/b via cpt-lookup */
						if(!strncmp (&p[3], "NaN", 3)) {	/* Got -ZNaN */
							GMT_get_rgb24 (GMT_d_NaN, fill.rgb);
							fill.use_pattern = FALSE;
							polygon = TRUE;
						}
						else {
							if (sscanf (&p[3], "%lg", &z) == 1) {
								GMT_get_rgb24 (z, fill.rgb);
								fill.use_pattern = FALSE;
								polygon = TRUE;
							}
						}
						if (!close_polygon) {	/* Use -Zz -Ccpt to set pen color instead */
							polygon = FALSE;
							ps_setpaint (fill.rgb);
						}
					}
					else {
						polygon = default_polygon;
						fill = default_fill;
					}
					if (S.G.label_type == 2) {	/* Update segment header */
						extract_label (&GMT_io.segment_header[1], S.G.label);
					}
					
					n_fields = GMT_input (fp, &n_expected, &in);
					n_total_read++;
				}
				if ((GMT_io.status & GMT_IO_EOF)) continue;	/* At EOF */

				n = 0;
				while (! (GMT_io.status & (GMT_IO_SEGMENT_HEADER | GMT_IO_EOF))) {	/* Keep going until FALSE or = 2 segment header */
					if (GMT_io.status & GMT_IO_MISMATCH) {
						fprintf (stderr, "%s: Mismatch between actual (%d) and expected (%d) fields near line %d\n", GMT_program, n_fields, n_expected, n_total_read);
						exit (EXIT_FAILURE);
					}

					xx[n] = in[0];	yy[n] = in[1];
					n++;
					if (n == n_alloc) {
						n_alloc += GMT_CHUNK;
						xx = (double *) GMT_memory ((void *)xx, (size_t)n_alloc, sizeof (double), GMT_program);
						yy = (double *) GMT_memory ((void *)yy, (size_t)n_alloc, sizeof (double), GMT_program);
					}
					n_fields = GMT_input (fp, &n_expected, &in);
				}
				if (polygon && !(xx[0] == xx[n-1] && yy[0] == yy[n-1])) {	/* Explicitly close polygon so that arc will work */
					xx[n] = xx[0];
					yy[n] = yy[0];
					n++;
					if (n == n_alloc) {
						n_alloc += GMT_CHUNK;
						xx = (double *) GMT_memory ((void *)xx, (size_t)n_alloc, sizeof (double), GMT_program);
						yy = (double *) GMT_memory ((void *)yy, (size_t)n_alloc, sizeof (double), GMT_program);
					}
				}
		
				xx = (double *) GMT_memory ((void *)xx, (size_t)n, sizeof (double), GMT_program);
				yy = (double *) GMT_memory ((void *)yy, (size_t)n, sizeof (double), GMT_program);
				n_alloc = n;
				
				if (!GMT_cpt_skip) {
					if (resample) n = GMT_fix_up_path (&xx, &yy, n, greenwich, step);

					if (polygon) {
						if ((plot_n = GMT_clip_to_map (xx, yy, n, &xp, &yp)) == 0) continue;
						while (plot_n > GMT_n_alloc) GMT_get_plot_array ();
						memcpy ((void *)GMT_x_plot, (void *)xp, (size_t)(plot_n * sizeof (double)));
						memcpy ((void *)GMT_y_plot, (void *)yp, (size_t)(plot_n * sizeof (double)));
					}
					else {
						if ((plot_n = GMT_geo_to_xy_line (xx, yy, n)) == 0) continue;
						xp = (double *) GMT_memory (VNULL, (size_t)plot_n, sizeof (double), GMT_program);
						yp = (double *) GMT_memory (VNULL, (size_t)plot_n, sizeof (double), GMT_program);
						plot_pen = (int *) GMT_memory (VNULL, (size_t)plot_n, sizeof (int), GMT_program);
						memcpy ((void *)xp, (void *)GMT_x_plot, (size_t)(plot_n * sizeof (double)));
						memcpy ((void *)yp, (void *)GMT_y_plot, (size_t)(plot_n * sizeof (double)));
						memcpy ((void *)plot_pen, (void *)GMT_pen, (size_t)(plot_n * sizeof (int)));
					}
		
					if (polygon) {
					
						if ((*GMT_will_it_wrap) (xp, yp, plot_n, &start)) {	/* Polygon wraps */
					
							/* First truncate agains left border */
						
							GMT_n_plot = (*GMT_truncate) (xp, yp, plot_n, start, -1);
							n_use = GMT_compact_line (GMT_x_plot, GMT_y_plot, GMT_n_plot, FALSE, 0);
							GMT_fill (GMT_x_plot, GMT_y_plot, n_use, &fill, outline);
								
							/* Then truncate agains right border */
						
							GMT_n_plot = (*GMT_truncate) (xp, yp, plot_n, start, +1);
							n_use = GMT_compact_line (GMT_x_plot, GMT_y_plot, GMT_n_plot, FALSE, 0);
							GMT_fill (GMT_x_plot, GMT_y_plot, n_use, &fill, outline);
					
						}
						else
						GMT_fill (xp, yp, plot_n, &fill, outline);
					}
					else if (S.symbol == QUOTED_LINE) {
						S.G.line_pen = pen;
						GMT_hold_contour (&xp, &yp, plot_n, "N/A", 'A', S.G.label_angle, close_polygon, &S.G);
					}
					else
						GMT_plot_line (xp, yp, plot_pen, plot_n);
										
					if (S.symbol == FRONT) GMT_draw_fence (xp, yp, plot_n, &S.f, &fill, outline); 	/* Must draw front crossbars */
					GMT_free ((void *)xp);
					GMT_free ((void *)yp);
					if (!polygon) GMT_free ((void *)plot_pen);
				}
			}
		}
		if (fp != GMT_stdin) GMT_fclose (fp);
	}
	
	if (S.symbol == QUOTED_LINE) GMT_contlabel_plot (&S.G);
	
	if (clip_set) GMT_map_clip_off ();

	GMT_world_map = old_GMT_world_map;
	
	GMT_map_basemap ();

	ps_plotend (gmtdefs.last_page);
	
	if (S.symbol == 0) {
		GMT_free ((void *)xx);
		GMT_free ((void *)yy);
	}
	
	GMT_end (argc, argv);
}

int decode_symbol_option (char *text, struct PSXY_SYMBOL *p)
{
	int decode_error = 0, slash, bset = 0, k, j, n, len, one, colon;
	BOOLEAN check, old_style;
	char symbol_type, txt_a[32], txt_b[32], txt_c[32], text_cp[128], *c;
	p->n_required = 0;	/* columns needed in addition to x & y */
	p->user_unit = p->convert_angles = p->shrink = p->read_vector = FALSE;

	if (!text) return -1;

	if (!text[0]) {	/* No symbol or size given */
		p->size = 0.0;
		symbol_type = '+';
	}
	else if (isdigit ((int)text[0]) || text[0] == '.') {	/* Size, but no symbol given */
		p->size = p->given_size = GMT_convert_units (text, GMT_INCH);
		symbol_type = '+';
	}
	else if (text[0] == 'l') {	/* Letter symbol is special case */
		strcpy (text_cp, text);
		if ((c = strchr (text_cp, '%'))) {	/* Gave font name or number, too */
			*c = ' ';	/* Make the % a space */
			c++;		/* Go to next character */
			if (c[0] >= '0' && c[0] <= '9')	/* Gave a font # */
				p->font_no = atoi (c);
			else
				p->font_no = GMT_font_lookup (c, GMT_font, N_FONTS);
			if (p->font_no >= N_FONTS) {
				fprintf (stderr, "%s: -Sl contains bad font (set to %s (0))\n", GMT_program, GMT_font[gmtdefs.annot_font[0]].name);
				p->font_no = gmtdefs.annot_font[0];
			}
		}
		if (text[1] == '/') {	/* No size given */
			symbol_type = 'l';
			if (p->size == 0.0) p->size = p->given_size;
		}
		else {
			n = sscanf (text_cp, "%c%[^/]/%s", &symbol_type, txt_a, p->string);
			p->given_size = p->size = GMT_convert_units (txt_a, GMT_INCH);
		}
	}
	else if (text[0] == 'k') {	/* Custom symbol spec */
		for (j = strlen (text); j > 0 && text[j] != '/'; j--);;
		if (j == 0) {	/* No slash, i.e., no symbol size given */
			if (p->size == 0.0) p->size = p->given_size;
			n = sscanf (text, "%c%s", &symbol_type, text_cp);
		}
		else {
			text[j] = ' ';
			n = sscanf (text, "%c%s %s", &symbol_type, text_cp, txt_a);
			text[j] = '/';
			p->given_size = p->size = GMT_convert_units (txt_a, GMT_INCH);
		}
	}
	else if (strchr ("-aAbCcDdeEfGgHhIiNnpqrSsTtVvwxy", (int) text[0]) && strchr ("CcIiMmPp", (int) text[1])) {	/* Symbol, but no size given (size assumed given as input data), only unit information provided here */
		n = sscanf (text, "%c", &symbol_type);
		if (p->size == 0.0) p->size = p->given_size;
		if (text[1] && (p->u = GMT_get_unit (text[1])) < 0) decode_error = TRUE;
	}
	else if (strchr ("-aAbCcDdeEfGgHhIiNnpqrSsTtVvwxy", (int) text[0]) && (text[1] == '\n' || text[1] == '\0')) {	/* Symbol, but no size given (size assumed given as input data) */
		n = sscanf (text, "%c", &symbol_type);
		if (p->size == 0.0) p->size = p->given_size;
	}
	else if (text[0] == 'b') {	/* Bar with size */
		for (j = 1, bset = 0; text[j] && !bset; j++) if (text[j] == 'b') bset = j;
		strcpy (text_cp, text);
		if (bset) text_cp[bset] = 0;	/* Chop off the b<base> from copy */
		if ((bset && text_cp[bset-1] == 'u') || (j && text[j-1] == 'u')) p->user_unit = TRUE;
		n = sscanf (text_cp, "%c%s", &symbol_type, txt_a);
		if (n == 2) {
			if (p->user_unit)
				p->size = atof (txt_a);
			else
				p->size = GMT_convert_units (txt_a, GMT_INCH);
			p->given_size = p->size;
		}
		else	/* No size given */
			if (p->size == 0.0) p->size = p->given_size;
	}
	else {
		n = sscanf (text, "%c%s", &symbol_type, txt_a);
		symbol_type = text[0];
		p->given_size = p->size = GMT_convert_units (txt_a, GMT_INCH);
	}

	check = TRUE;
	switch (symbol_type) {
		case '+':
			p->symbol = UNSPECIFIED;
			break;
		case '-':
			p->symbol = XDASH;
			p->size *= 0.5;		/* We will use +- to get full width */
			break;
		case 'A':
			p->size *= 1.6728932614;	/* To equal area of circle with same size */
		case 'a':
			p->symbol = STAR;
			break;
		case 'b':
			p->symbol = BAR;
			p->size *= 0.5;		/* We will use +- to get full width */
			if (bset) p->base = atof (&text[bset+1]);
			break;
		case 'C':
		case 'c':
			p->symbol = CIRCLE;
			break;
		case 'D':
			p->size *= 1.2533141373;	/* To equal area of circle with same size */
		case 'd':
			p->symbol = DIAMOND;
			break;
		case 'E':	/* Expect axis in km to be scaled based on -J */
			p->convert_angles = 1;
		case 'e':
			p->symbol = ELLIPSE;
			p->n_required = 3;
			check = FALSE;
			break;
		case 'f':	/* Fronts:   -Sf<spacing>/<size>[dir][type][:<offset>]	*/
			p->symbol = FRONT;
			p->f.f_off = 0.0;
			strcpy (text_cp, text);
			if ((c = strchr (text_cp, ':'))) {	/* Gave :<offset>, set it and strip it off */
				c++;	/* SKip over the colon */
				p->f.f_off = GMT_convert_units (c, GMT_INCH);
				c--;	/* Go back to colon */
				*c = 0;	/* Effectively chops off the offset modifier */
			}
			len = strlen (text_cp) - 1;
			
			old_style = FALSE;
			switch (text_cp[len]) {
				case 'f':	/* Fault front */
					p->f.f_symbol = F_FAULT;
					len--;
					break;
				case 't':	/* Triangle front */
					p->f.f_symbol = F_TRIANGLE;
					len--;
					break;
				case 's':	/* Strike-slip front */
					p->f.f_symbol = F_SLIP;
					len--;
					break;
				case 'c':	/* [half-]circle front */
					p->f.f_symbol = F_CIRCLE;
					len--;
					break;
				case 'b':	/* [half-]square front */
					p->f.f_symbol = F_BOX;
					len--;
					break;
					
				/* Old style (backward compatibility) */
				
				case 'L':	/* Left triangle */
					p->f.f_symbol = F_TRIANGLE;
				case 'l':	/* Left ticked fault */
					p->f.f_sense = F_LEFT;
					old_style = TRUE;
					break;
				case 'R':	/* Right triangle */
					p->f.f_symbol = F_TRIANGLE;
				case 'r':	/* Right ticked fault */
					p->f.f_sense = F_RIGHT;
					old_style = TRUE;
					break;
				default:
					p->f.f_sense = F_CENTERED;
					break;
			}

			if (!old_style) {
				switch (text_cp[len]) {	/* Get sense - default is centered */
					case 'l':
						p->f.f_sense = F_LEFT;
						break;
					case 'r':
						p->f.f_sense = F_RIGHT;
						break;
					default:
						len++;
						p->f.f_sense = F_CENTERED;
						if (p->f.f_symbol == F_SLIP) {
							fprintf (stderr, "%s: Error in Option -Sf: Must specify (l)eft-lateral or (r)ight-lateral slip\n", GMT_program);
							exit (EXIT_FAILURE);
						}
						break;
				}
			}

			text_cp[len] = 0;	/* Gets rid of the [dir][type] flags, if present */
			
			/* Pull out and get spacing and size */
			
			sscanf (&text_cp[1], "%[^/]/%s", txt_a, txt_b);
			p->f.f_gap = GMT_convert_units (txt_a, GMT_INCH);
			p->f.f_len = GMT_convert_units (txt_b, GMT_INCH);
			break;
		case 'H':
			p->size *= 1.09963611078;	/* To equal area of circle with same size */
		case 'h':
			p->symbol = HEXAGON;
			break;
		case 'I':
			p->size *= 1.55512030155;	/* To equal area of circle with same size */
		case 'i':
			p->symbol = ITRIANGLE;
			break;
		case 'l':
			p->symbol = TEXT;
			/* Look for a slash that separates size and string: */
			for (j = 1, slash = 0; text_cp[j] && !slash; j++) if (text_cp[j] == '/') slash = j;
			/* Set j to the first char in the string: */
			j = slash + 1;
			/* Copy string characters */
			k = 0;
			while (text_cp[j] && text_cp[j] != ' ' && k < 63) p->string[k++] = text_cp[j++];
			if (!k) {
				fprintf (stderr, "%s: GMT SYNTAX ERROR -Sl option:  No string given\n", GMT_program);
				decode_error++;
			}
			p->string[k] = 0;
			break;
		case 'N':
			p->size *= 1.32130639968;	/* To equal area of circle with same size */
		case 'n':
			p->symbol = PENTAGON;
			break;
		case 'G':
			p->size *= 0.94805944897;	/* To equal area of circle with same size */
		case 'g':
			p->symbol = OCTAGON;
			break;
		case 'p':
			p->symbol = POINT;
			check = FALSE;
			break;
		case 'q':	/* Quoted lines: -Sq[d|n|l|x]<info>[:<labelinfo>] */
			p->symbol = QUOTED_LINE;
			for (j = 1, colon = 0; text[j]; j++) if (text[j] == ':') colon = j;
			if (colon) {	/* Gave :<labelinfo> */
				text[colon] = 0;
				decode_error += GMT_contlabel_info ('S', &text[1], &p->G);
				decode_error += GMT_contlabel_specs (&text[colon+1], &p->G);
			}
			else
				decode_error += GMT_contlabel_info ('S', &text[1], &p->G);
			check = FALSE;
			break;
		case 'S':
			p->size *= 1.25331413731;	/* To equal area of circle with same size */
		case 's':
			p->symbol = SQUARE;
			break;
		case 'T':
			p->size *= 1.55512030155;	/* To equal area of circle with same size */
		case 't':
			p->symbol = TRIANGLE;
			break;
		case 'V':
			p->convert_angles = 1;
		case 'v':
			p->symbol = VECTOR;
			p->v_double_heads = FALSE;
			switch (text[1]) {	/* Check if s(egment), h(ead), b(alance center), or t(ail) have been specified */
				case 'S':	/* Input (x,y) refers to vector head (the tip), double heads */
					p->v_double_heads = TRUE;
				case 's':	/* Input (x,y) refers to vector head (the tip), single head  */
					p->v_just = 3;
					one = 2;
					break;
				case 'H':	/* Input (x,y) refers to vector head (the tip), double heads */
					p->v_double_heads = TRUE;
				case 'h':	/* Input (x,y) refers to vector head (the tip), single head */
					p->v_just = 2;
					one = 2;
					break;
				case 'B':	/* Input (x,y) refers to balance point of vector, double heads */
					p->v_double_heads = TRUE;
				case 'b':	/* Input (x,y) refers to balance point of vector, single head */
					p->v_just = 1;
					one = 2;
					break;
				case 'T':	/* Input (x,y) refers to tail of vector, double heads */
					p->v_double_heads = TRUE;
				case 't':	/* Input (x,y) refers to tail of vector [Default], single head */
					p->v_just = 0;
					one = 2;
					break;
				default:	/* No modifier given, default to tail, single head */
					p->v_just = 0;
					one = 1;
					break;
			}
			for (j = one; text[j] && text[j] != 'n'; j++);
			len = strlen(text) - 1;
			if (text[j] == 'n') {	/* Normalize option used */
				k = GMT_get_unit (text[len]);
				if (k >= 0) p->u = k;
				p->v_norm = GMT_convert_units (&text[j+1], GMT_INCH);
				if (p->v_norm > 0.0) {
					p->v_shrink = 1.0 / p->v_norm;
					p->symbol = VECTOR2;
				}
				text[j] = 0;	/* Chop of the n<shrink> part */
			}
			if (text[one]) {
				/* It is possible that the user have appended a unit modifier after
				 * the <size> argument (which here are vector attributes).  We use that
				 * to set the unit, but only if the vector attributes themselves have
				 * units. (If not we would override MEASURE_UNIT without cause).
				 * So, -SV0.1i/0.2i/0.3ic will expect 4th column to have length in cm
				 * while SV0.1i/0.2i/0.3i expects data units in MEASURE_UNIT
				 */

				if (isalpha ((int)text[len]) && isalpha ((int)text[len-1])) {
					p->u = GMT_get_unit (text[len]);
					text[len] = 0;
				}
				sscanf (&text[one], "%[^/]/%[^/]/%s", txt_a, txt_b, txt_c);
				p->v_width  = GMT_convert_units (txt_a, GMT_INCH);
				p->h_length = GMT_convert_units (txt_b, GMT_INCH);
				p->h_width  = GMT_convert_units (txt_c, GMT_INCH);
			}
			if (p->symbol == VECTOR2) text[j] = 'n';	/* Put back the n<shrink> part */
			p->read_vector = TRUE;
			p->n_required = 2;
			check = FALSE;
			break;
		case 'w':
			p->symbol = PIE;
			p->n_required = 2;
			break;
		case 'r':
			p->symbol = RECT;
			p->n_required = 2;
			check = FALSE;
			break;
		case 'x':
			p->symbol = CROSS;
			break;
		case 'y':
			p->symbol = YDASH;
			p->size *= 0.5;		/* We will use +- to get full width */
			break;
		case 'k':
			p->symbol = CUSTOM;
			p->custom = GMT_get_custom_symbol (text_cp);
			break;
		default:
			decode_error = TRUE;
			fprintf (stderr, "%s: GMT SYNTAX ERROR -S option:  Unrecognized symbol type %c\n", GMT_program, symbol_type);
			break;
	}
	if (p->given_size == 0.0 && check) {
		p->read_size = TRUE;
		p->n_required++;
	}
	else
		p->read_size = FALSE;

	return (decode_error);
}

#define GMT_ELLIPSE_APPROX 72

void GMT_plot_ellipse (double lon, double lat, double major, double minor, double azimuth, struct GMT_FILL fill, int outline)
{
	/* GMT_plot_ellipse takes the location, axes (in km), and azimuth of an ellipse
	   and draws the ellipse using the chosen map projection */

	int i, plot_n, start, n_use;
	double delta_azimuth, sin_azimuth, cos_azimuth, sinp, cosp, angle, x, y, x_prime, y_prime, rho, c;
	double sin_c, cos_c; 
	double *px, *py, *xp, *yp;

	px = (double *) GMT_memory (VNULL, (size_t)GMT_ELLIPSE_APPROX, sizeof (double), GMT_program);
	py = (double *) GMT_memory (VNULL, (size_t)GMT_ELLIPSE_APPROX, sizeof (double), GMT_program);

	delta_azimuth = 2.0 * M_PI / GMT_ELLIPSE_APPROX;
	major *= 1000.0;	minor *= 1000.0;	/* Convert to meters */
	azimuth = 90.0 - azimuth;	/* Because the code below originally used directions instead */
	azimuth *= D2R;
	sincos (azimuth, &sin_azimuth, &cos_azimuth);
	sincos (lat * D2R, &sinp, &cosp);	/* Set up azimuthal equidistant projection */

/*	if (project_info.central_meridian < project_info.w && (project_info.central_meridian + 360.0) <= project_info.e) project_info.central_meridian += 360.0;
	if (project_info.central_meridian > project_info.e && (project_info.central_meridian - 360.0) >= project_info.w) project_info.central_meridian -= 360.0;
*/
	/* Approximate ellipse by a GMT_ELLIPSE_APPROX-sided polygon */

	for (i = 0; i < GMT_ELLIPSE_APPROX; i++) {

		angle = i * delta_azimuth;

		sincos (angle, &y, &x);
		x *= major;
		y *= minor;

		/* Get rotated coordinates in m */

		x_prime = x * cos_azimuth - y * sin_azimuth;
		y_prime = x * sin_azimuth + y * cos_azimuth;
		
		/* Convert m back to lon lat */

		rho = hypot (x_prime, y_prime);
	
		c = rho / project_info.EQ_RAD;
		sincos (c, &sin_c, &cos_c);
		py[i] = d_asin (cos_c * sinp + (y_prime * sin_c * cosp / rho)) * R2D;
		if ((lat - 90.0) > -GMT_CONV_LIMIT)
			px[i] = lon + R2D * d_atan2 (x_prime, -y_prime);
		else if ((lat + 90.0) < GMT_CONV_LIMIT)
			px[i] = lon + R2D * d_atan2 (x_prime, y_prime);
		else
			px[i] = lon +
				R2D * d_atan2 (x_prime * sin_c, (rho * cosp * cos_c - y_prime * sinp * sin_c));
		while ((px[i] - project_info.central_meridian) < -180.0) px[i] += 360.0;
		while ((px[i] - project_info.central_meridian) > 180.0) px[i] -= 360.0;
	}

	if ((plot_n = GMT_clip_to_map (px, py, GMT_ELLIPSE_APPROX, &xp, &yp)) == 0) return;

	while (plot_n > GMT_n_alloc) GMT_get_plot_array ();
	memcpy ((void *)GMT_x_plot, (void *)xp, (size_t)(plot_n * sizeof (double)));
	memcpy ((void *)GMT_y_plot, (void *)yp, (size_t)(plot_n * sizeof (double)));

	if ((*GMT_will_it_wrap) (xp, yp, plot_n, &start)) {	/* Polygon wraps */
					
		/* First truncate agains left border */
						
		GMT_n_plot = (*GMT_truncate) (xp, yp, plot_n, start, -1);
		n_use = GMT_compact_line (GMT_x_plot, GMT_y_plot, GMT_n_plot, FALSE, 0);
		GMT_fill (GMT_x_plot, GMT_y_plot, n_use, &fill, outline);
								
		/* Then truncate agains right border */
						
		GMT_n_plot = (*GMT_truncate) (xp, yp, plot_n, start, +1);
		n_use = GMT_compact_line (GMT_x_plot, GMT_y_plot, GMT_n_plot, FALSE, 0);
		GMT_fill (GMT_x_plot, GMT_y_plot, n_use, &fill, outline);
						
	}
	else
		GMT_fill (xp, yp, plot_n, &fill, outline);

	GMT_free ((void *)xp);
	GMT_free ((void *)yp);
	GMT_free ((void *)px);
	GMT_free ((void *)py);
}

void GMT_draw_fence (double x[], double y[], int n, struct FRONTLINE *f, struct GMT_FILL *g, BOOLEAN outline)
{
	int i, ngap;
	BOOLEAN skip;
	double *s, xx[4], yy[4], dist, w, frac, dx, dy, angle, dir1, dir2;
	double gap, x0, y0, len2, len3, cosa, sina, sa, ca, offx, offy;
	
	if (n < 2) return;

	s = (double *) GMT_memory (VNULL, (size_t)n, sizeof (double), GMT_program);
	for (i = 1, s[0] = 0.0; i < n; i++) {
		/* Watch out for longitude wraps */
		dx = x[i] - x[i-1];
		w = GMT_half_map_width (y[i]);
		if (GMT_world_map && dx > w) dx = copysign (2 * w - fabs (dx), -dx);
		s[i] = s[i-1] + hypot (dx, y[i] - y[i-1]);
	}
		
	if (f->f_gap > 0.0) {	/* Gap distance given, calculate number of them and round off gap distance */
		ngap = irint (s[n-1] / f->f_gap);
		gap = s[n-1] / (double)ngap;
		dist = f->f_off;
		ngap++;
	}
	else {			/* Number of gaps given, calculate gap distance */
		ngap = (int) fabs (f->f_gap);
		gap = s[n-1] / (ngap - 1);
		if (ngap == 1) dist = 0.5 * s[n-1];
	}
	
	len2 = 0.5 * f->f_len;
	len3 = 0.866025404 * f->f_len;	
	if (f->f_sense == F_CENTERED) len3 = len2;
	
	i = 0;
	while (i < n) {
		while ((s[i] - dist) > -SMALL) {	/* Time for tick */
			if (i > 0) {
				dx = x[i] - x[i-1];
				dy = y[i] - y[i-1];
			}
			else {
				dx = x[1] - x[0];
				dy = y[1] - y[0];
			}
			if (fabs (dist - s[i]) < SMALL) {
				x0 = x[i];
				y0 = y[i];
			}
			else {
				frac = (s[i] - dist) / (s[i] - s[i-1]);
				x0 = x[i] - dx * frac;
				y0 = y[i] - dy * frac;
			}
			angle = d_atan2 (dy, dx);
			skip = (GMT_world_map && fabs (dx) > GMT_half_map_width (y[i]));	/* Dont do ticks on jumps */
			if (skip) {
				dist += gap;
				i++;
				continue;
			}
			
			switch (f->f_symbol) {
				case F_TRIANGLE:	/* Triangle */	
					switch (f->f_sense) {
						case F_CENTERED:
							sincos (angle, &sina, &cosa);
							xx[0] = x0 + len2 * cosa;
							yy[0] = y0 + len2 * sina;
							xx[1] = x0 - len3 * sina;
							yy[1] = y0 + len3 * cosa;
							xx[2] = x0 - len2 * cosa;
							yy[2] = y0 - len2 * sina;
							xx[3] = x0 + len3 * sina;
							yy[3] = y0 - len3 * cosa;
							ps_patch (xx, yy, 4, g->rgb, outline);
							break;
						case F_RIGHT:
							angle += M_PI;
						case F_LEFT:
							sincos (angle, &sina, &cosa);
							xx[0] = x0 + len2 * cosa;
							yy[0] = y0 + len2 * sina;
							xx[1] = x0 - len3 * sina;
							yy[1] = y0 + len3 * cosa;
							xx[2] = x0 - len2 * cosa;
							yy[2] = y0 - len2 * sina;
							ps_patch (xx, yy, 3, g->rgb, outline);
							break;
					}
					break;
				
				case F_CIRCLE:	/* Circles */	
					switch (f->f_sense) {
						case F_CENTERED:
							ps_circle (x0, y0, f->f_len, g->rgb, outline);
							break;
						case F_RIGHT:
							angle += M_PI;
						case F_LEFT:
							angle *= R2D;
							dir1 = angle;
							dir2 = angle + 180.0;
							if (dir1 > dir2) dir1 -= 360.0;
							ps_pie (x0, y0, len2, dir1, dir2, g->rgb, outline);
							break;
					}
					break;
				
				case F_BOX:	/* Squares */	
					switch (f->f_sense) {
						case F_CENTERED:	/* Full square centered on line */
							sincos (angle, &sina, &cosa);
							xx[0] = x0 + len2 * (cosa + sina);	/* LR */
							yy[0] = y0 + len2 * (sina - cosa);
							xx[1] = x0 + len2 * (cosa - sina);	/* UR */
							yy[1] = y0 + len2 * (sina + cosa);
							xx[2] = x0 + len2 * (-cosa - sina);	/* UL */
							yy[2] = y0 + len2 * (-sina + cosa);
							xx[3] = x0 + len2 * (-cosa + sina);	/* LL */
							yy[3] = y0 + len2 * (-sina - cosa);
							break;
						case F_RIGHT:
							angle += M_PI;
						case F_LEFT:
							/* Half square on the chosen side */
							sincos (angle, &sina, &cosa);
							xx[0] = x0 + len2 * (cosa);	/* LR */
							yy[0] = y0 + len2 * (sina);
							xx[1] = x0 + len2 * (cosa - sina);	/* UR */
							yy[1] = y0 + len2 * (sina + cosa);
							xx[2] = x0 + len2 * (-cosa - sina);	/* UL */
							yy[2] = y0 + len2 * (-sina + cosa);
							xx[3] = x0 + len2 * (-cosa);	/* LL */
							yy[3] = y0 + len2 * (-sina);
							break;
					}
					ps_patch (xx, yy, 4, g->rgb, outline);
					break;
				
				case F_SLIP: /* draw strike-slip arrows */
					sincos (angle, &sina, &cosa);
				        offx = gmtdefs.annot_offset[0] * sina; /* get offsets from front line */
					offy = gmtdefs.annot_offset[0] * cosa;
					/* sense == F_LEFT == left-lateral, R_RIGHT = right lateral */
					/* arrow "above" line */
					xx[0] = x0 + f->f_sense * offx;
					yy[0] = y0 - f->f_sense * offy;
					xx[1] = xx[0] - len2 * cosa;
					yy[1] = yy[0] - len2 * sina;
					xx[2] = xx[0] + len2 * cosa;
					yy[2] = yy[0] + len2 * sina;
					ps_plot (xx[1], yy[1], 3);
					ps_plot (xx[2], yy[2], 2);
					sincos (angle + (f->f_sense * 30.0 * D2R), &sa, &ca);
					xx[3] = xx[2] - len2 * ca;
					yy[3] = yy[2] - len2 * sa;
					ps_plot (xx[3], yy[3], 2);

					/* arrow "below" line */
					xx[0] = x0 - f->f_sense * offx;
					yy[0] = y0 + f->f_sense * offy;
					xx[1] = xx[0] + len2 * cosa;
					yy[1] = yy[0] + len2 * sina;
					xx[2] = xx[0] - len2 * cosa;
					yy[2] = yy[0] - len2 * sina;
					ps_plot (xx[1], yy[1], 3);
					ps_plot (xx[2], yy[2], 2);
					sincos (angle - (f->f_sense * 150.0 *D2R), &sa, &ca);
					xx[3] = xx[2] - len2 * ca;
					yy[3] = yy[2] - len2 * sa;
					ps_plot (xx[3], yy[3], -2); 
					break;
					
				case F_FAULT:	/* Normal fault ticks */	
					xx[0] = xx[1] = x0;	yy[0] = yy[1] = y0;
					if (f->f_sense == F_CENTERED) {
						angle -= M_PI_2;
						sincos (angle, &sina, &cosa);
						xx[0] += len2 * cosa;
						yy[0] += len2 * sina;
						xx[1] -= len2 * cosa;
						yy[1] -= len2 * sina;
					}
					else {
						angle += (f->f_sense * M_PI_2);
						sincos (angle, &sina, &cosa);
						xx[1] += len2 * cosa;
						yy[1] += len2 * sina;
					}
					ps_plot (xx[0], yy[0], 3);
					ps_plot (xx[1], yy[1], -2);
					break;
			}
			dist += gap;
		}
		i++;
	}
	GMT_free ((void *)s);
}

void plot_x_errorbar (double x, double y, double delta_x, double error_width, double error_width2, int line) {
	double x_1, x_2, y_1, y_2;
	
	GMT_geo_to_xy (x - delta_x, y, &x_1, &y_1);
	GMT_geo_to_xy (x + delta_x, y, &x_2, &y_2);
	if (GMT_is_dnan (x_1)) {
		fprintf (stderr, "%s: Warning: X error bar exceeded domain near line %d. Set to x_min\n", GMT_program, line);
		x_1 = project_info.xmin;
	}
	if (GMT_is_dnan (x_2)) {
		fprintf (stderr, "%s: Warning: X error bar exceeded domain near line %d. Set to x_max\n", GMT_program, line);
		x_2 = project_info.xmax;
	}
	ps_plot (x_1, y_1, 3);
	ps_plot (x_2, y_2, -2);
	if (error_width > 0.0) {
		ps_plot (x_1, y_1 - error_width2, 3);
		ps_plotr (0.0, error_width, 2);
		ps_plot (x_2, y_2 - error_width2, 3);
		ps_plotr (0.0, error_width, -2);
	}
}

void plot_y_errorbar (double x, double y, double delta_y, double error_width, double error_width2, int line) {
	double x_1, x_2, y_1, y_2;

	GMT_geo_to_xy (x, y - delta_y, &x_1, &y_1);
	GMT_geo_to_xy (x, y + delta_y, &x_2, &y_2);
	if (GMT_is_dnan (y_1)) {
		fprintf (stderr, "%s: Warning: Y error bar exceeded domain near line %d. Set to y_min\n", GMT_program, line);
		y_1 = project_info.ymin;
	}
	if (GMT_is_dnan (y_2)) {
		fprintf (stderr, "%s: Warning: Y error bar exceeded domain near line %d. Set to y_max\n", GMT_program, line);
		y_2 = project_info.ymax;
	}
	ps_plot (x_1, y_1, 3);
	ps_plot (x_2, y_2, -2);
	if (error_width > 0.0) {
		ps_plot (x_1 - error_width2, y_1, 3);
		ps_plotr (error_width, 0.0, 2);
		ps_plot (x_2 - error_width2, y_2, 3);
		ps_plotr (error_width, 0.0, -2);
	}
}

void plot_x_whiskerbar (double x, double y, double hinge[], double error_width, double error_width2, int rgb[], int line) {
	int i;
	static int q[4] = {0, 25, 75, 100};
	double xx[4], yy[4];
	
	for (i = 0; i < 4; i++) {	/* for 0, 25, 75, 100% hinges */
		GMT_geo_to_xy (hinge[i], y, &xx[i], &yy[i]);
		if (GMT_is_dnan (xx[i])) {
			fprintf (stderr, "%s: Warning: X %d %% hinge exceeded domain near line %d\n", GMT_program, q[i], line);
			xx[i] = (i <2 ) ? project_info.xmin :  project_info.xmax;
		}
	}
	yy[1] -= error_width2;
	yy[2] += error_width2;
	
	ps_plot (xx[0], yy[1], 3);		/* Left whisker */
	ps_plotr (0.0, error_width, 2);
	ps_plot (xx[0], yy[0], 3);
	ps_plot (xx[1], yy[0], -2);
	
	ps_plot (xx[3], yy[1], 3);		/* Right whisker */
	ps_plotr (0.0, error_width, 2);
	ps_plot (xx[3], yy[0], 3);
	ps_plot (xx[2], yy[0], -2);

	ps_rect (xx[1], yy[1], xx[2], yy[2], rgb, TRUE);	/* Main box */
	
	ps_plot (x, yy[1], 3);			/* Median line */
	ps_plot (x, yy[2], -2);
}

void plot_y_whiskerbar (double x, double y, double hinge[], double error_width, double error_width2, int rgb[], int line) {
	int i;
	static int q[4] = {0, 25, 75, 100};
	double xx[4], yy[4];
	
	for (i = 0; i < 4; i++) {	/* for 0, 25, 75, 100% hinges */
		GMT_geo_to_xy (x, hinge[i], &xx[i], &yy[i]);
		if (GMT_is_dnan (yy[i])) {
			fprintf (stderr, "%s: Warning: Y %d %% hinge exceeded domain near line %d\n", GMT_program, q[i], line);
			yy[i] = (i <2 ) ? project_info.ymin :  project_info.ymax;
		}
	}
	xx[1] -= error_width2;
	xx[2] += error_width2;
	
	ps_plot (xx[1], yy[0], 3);		/* bottom whisker */
	ps_plotr (error_width, 0.0, 2);
	ps_plot (xx[0], yy[0], 3);
	ps_plot (xx[0], yy[1], -2);
	
	ps_plot (xx[1], yy[3], 3);		/* Top whisker */
	ps_plotr (error_width, 0.0, 2);
	ps_plot (xx[0], yy[3], 3);
	ps_plot (xx[0], yy[2], -2);

	ps_rect (xx[2], yy[2], xx[1], yy[1], rgb, TRUE);	/* Main box */
	
	ps_plot (xx[1], y, 3);			/* Median line */
	ps_plot (xx[2], y, -2);
}

void extract_label (char *line, char *label)
{
	int i = 0, j, j0;
	char *p;
	
	if ((p = strstr (line, " -L")) || (p = strstr (line, "	-L"))) 	/* Get label specified wih -L option */
		i = p + 3 - line;
	else {								/* Bypass whitespace and pick first word */
		while (line[i] && (line[i] == ' ' || line[i] == '\t')) i++;
		
	}
	if ((p = strchr (&line[i], '\"'))) {	/* Gave several words as label */
		for (j0 = i, j = i + 1; line[j] != '\"'; j++);
		if (line[j] == '\"')	/* Found the matching quote */
			strncpy (label, &line[j0], j-j0);
		else {			/* Missing the matching quote */
			sscanf (&line[i], "%s", label);
			fprintf (stderr, "%s: Warning: Label (%s) not terminated by matching quote\n", GMT_program, label);
		}
	}
	else
		sscanf (&line[i], "%s", label);
}

