graphics_regis.c revision 01037d57
1/* $XTermId: graphics_regis.c,v 1.62 2014/12/23 01:39:00 tom Exp $ */
2
3/*
4 * Copyright 2014 by Ross Combs
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 */
32
33#include <xterm.h>
34
35#include <stdio.h>
36#include <ctype.h>
37#include <math.h>
38#include <stdlib.h>
39
40#if OPT_DOUBLE_BUFFER
41#include <X11/extensions/Xdbe.h>
42#endif
43
44#include <data.h>
45#include <VTparse.h>
46#include <ptyx.h>
47
48#include <assert.h>
49#include <graphics.h>
50#include <graphics_regis.h>
51
52/* get rid of shadowing warnings (we will not draw Bessel functions) */
53#define y1 my_y1
54#define y0 my_y0
55
56#define IS_HEX_DIGIT(CH) ( \
57  (CH) == '0' || \
58  (CH) == '1' || \
59  (CH) == '2' || \
60  (CH) == '3' || \
61  (CH) == '4' || \
62  (CH) == '5' || \
63  (CH) == '6' || \
64  (CH) == '7' || \
65  (CH) == '8' || \
66  (CH) == '9' || \
67  (CH) == 'a' || \
68  (CH) == 'b' || \
69  (CH) == 'c' || \
70  (CH) == 'd' || \
71  (CH) == 'e' || \
72  (CH) == 'f' || \
73  (CH) == 'A' || \
74  (CH) == 'B' || \
75  (CH) == 'C' || \
76  (CH) == 'D' || \
77  (CH) == 'E' || \
78  (CH) == 'F' )
79
80#define SCALE_FIXED_POINT 16U
81
82#undef DEBUG_ALPHABETS
83#undef DEBUG_BEZIER
84#undef DEBUG_SPLINE_SEGMENTS
85#undef DEBUG_SPLINE_POINTS
86#undef DEBUG_SPLINE_WITH_ROTATION
87#undef DEBUG_SPLINE_WITH_OVERDRAW
88#undef DEBUG_ARC_CENTER
89#undef DEBUG_ARC_START
90#undef DEBUG_ARC_END
91#undef DEBUG_SPECIFIC_CHAR_METRICS
92#define IS_DEBUG_CHAR(CH) ((CH) == 'W')		/* glyphs to dump to terminal */
93#undef DEBUG_COMPUTED_FONT_METRICS
94#undef DEBUG_FONT_NAME
95#undef DEBUG_FONT_SIZE_SEARCH
96#undef DEBUG_XFT_GLYPH
97#undef DEBUG_LOAD
98
99/* controls for extensions over VT3x0 limitations */
100#define ENABLE_RGB_COLORSPECS
101#define ENABLE_FREE_ROTATION
102#define ENABLE_UPLOAD_ALPHABET_FROM_FONT
103#define ENABLE_UPLOAD_ALPHABET_ZERO
104#define ENABLE_USER_FONT_SIZE
105#define ENABLE_VARIABLE_ITALICS
106
107#define MIN_ITERATIONS_BEFORE_REFRESH 20U
108#define MIN_MS_BEFORE_REFRESH 30
109/* *INDENT-OFF* */
110typedef struct RegisPoint {
111    int  x, y;
112} RegisPoint;
113
114typedef struct RegisWriteControls {
115    unsigned     pv_multiplier;
116    unsigned     pattern;
117    unsigned     pattern_multiplier;
118    unsigned     invert_pattern;
119    unsigned     plane_mask;
120    unsigned     write_style;
121    RegisterNum  foreground;
122    unsigned     shading_enabled;
123    char         shading_character;
124    int          shading_reference;
125    unsigned     shading_reference_dim;
126    unsigned     line_width;
127} RegisWriteControls;
128
129typedef struct RegisTextControls {
130    unsigned  alphabet_num;
131    unsigned  character_set_l; /* default: "(B" (ASCII) */
132    unsigned  character_set_r; /* default: "-@" (Latin-1) */
133    unsigned  character_display_w;
134    unsigned  character_display_h;
135    unsigned  character_unit_cell_w;
136    unsigned  character_unit_cell_h;
137    int       character_inc_x;
138    int       character_inc_y;
139    int       string_rotation;
140    int       character_rotation;
141    int       slant; /* for italic/oblique */
142} RegisTextControls;
143
144#define FixedCopy(dst, src, len) strncpy(dst, src, len - 1)[len - 1] = '\0'
145#define CopyFontname(dst, src) FixedCopy(dst, src, REGIS_FONTNAME_LEN)
146
147#define MAX_REGIS_ALPHABETS 8U
148#define REGIS_ALPHABET_NAME_LEN 11U
149#define REGIS_FONTNAME_LEN 256U
150/* enough for a 16x24 font (about 100KB) */
151#define MAX_REGIS_ALPHABET_BYTES (256U * 16U * 24U)
152#define MAX_GLYPH_PIXELS 8192U
153#define MAX_GLYPHS 256U
154#define INVALID_ALPHABET_NUM ~0U
155
156typedef struct RegisAlphabet {
157    unsigned       alphabet_num;
158    unsigned       pixw, pixh;
159    char           name[REGIS_ALPHABET_NAME_LEN];
160    char           fontname[REGIS_FONTNAME_LEN];
161    int            use_font;
162    int            loaded[MAX_GLYPHS];
163    unsigned char *bytes;
164} RegisAlphabet;
165
166typedef struct RegisDataFragment {
167    char const  *start;
168    unsigned     pos;
169    unsigned     len;
170} RegisDataFragment;
171/* *INDENT-ON* */
172
173#define POSITION_STACK_SIZE 16U
174#define DUMMY_STACK_X -32768
175#define DUMMY_STACK_Y -32768
176
177#define CURVE_POSITION_ARC_EDGE     0U
178#define CURVE_POSITION_ARC_CENTER   1U
179#define CURVE_POSITION_OPEN_CURVE   2U
180#define CURVE_POSITION_CLOSED_CURVE 3U
181
182#define MAX_INPUT_CURVE_POINTS 16U
183#define MAX_CURVE_POINTS (MAX_INPUT_CURVE_POINTS + 4U)
184
185#define MAX_FILL_POINTS 2048U
186
187typedef struct RegisParseState {
188    RegisDataFragment input;
189    char *temp;
190    unsigned templen;
191    char command;
192    char option;
193    /* position stack */
194    int stack_x[POSITION_STACK_SIZE];
195    int stack_y[POSITION_STACK_SIZE];
196    unsigned stack_next;	/* next empty position */
197    /* curve options */
198    int curve_mode;
199    int arclen;
200    int x_points[MAX_CURVE_POINTS];
201    int y_points[MAX_CURVE_POINTS];
202    unsigned num_points;
203    /* load options */
204    char load_name[REGIS_ALPHABET_NAME_LEN];
205    unsigned load_alphabet;
206    unsigned load_w, load_h;
207    unsigned load_index;
208    unsigned load_glyph;
209    unsigned load_row;
210    /* text options */
211    int string_rot_set;		/* flag to distinguish string vs. character rotation */
212} RegisParseState;
213
214typedef struct RegisGraphicsContext {
215    Graphic *graphic;
216    int terminal_id;
217    int x_off, y_off;
218    int x_div, y_div;
219    int width, height;
220    unsigned all_planes;
221    RegisterNum background;
222    char const *builtin_font;
223    RegisAlphabet alphabets[MAX_REGIS_ALPHABETS];
224    RegisWriteControls persistent_write_controls;
225    RegisWriteControls temporary_write_controls;
226    RegisTextControls persistent_text_controls;
227    RegisTextControls temporary_text_controls;
228    RegisTextControls *current_text_controls;
229    int multi_input_mode;
230    int graphics_output_cursor_x;
231    int graphics_output_cursor_y;
232    unsigned pattern_count;
233    unsigned pattern_bit;
234    int fill_mode;
235    RegisPoint fill_points[MAX_FILL_POINTS];
236    unsigned fill_point_count;
237} RegisGraphicsContext;
238
239static RegisGraphicsContext persistent_context;
240
241#define MAX_PATTERN_BITS 8U
242
243#define WRITE_STYLE_OVERLAY 1U
244#define WRITE_STYLE_REPLACE 2U
245#define WRITE_STYLE_COMPLEMENT 3U
246#define WRITE_STYLE_ERASE 4U
247
248#define WRITE_SHADING_REF_Y 0U
249#define WRITE_SHADING_REF_X 1U
250
251/* keypress event example: http://iraf.net/forum/viewtopic.php?showtopic=61692 */
252
253#define MIN2(X, Y) ( (X) < (Y) ? (X) : (Y) )
254#define MIN3(X, Y, Z) ( MIN2(MIN2((X), (Y)), MIN2((Y), (Z))) )
255#define MAX2(X, Y) ( (X) > (Y) ? (X) : (Y) )
256#define MAX3(X, Y, Z) ( MAX2(MAX2((X), (Y)), MAX2((Y), (Z))) )
257
258#define ROT_LEFT_N(V, N) ( (((V) << ((N) & 3U )) & 255U) | \
259			   ((V) >> (8U - ((N) & 3U))) )
260#define ROT_LEFT(V) ( (((V) << 1U) & 255U) | ((V) >> 7U) )
261
262#define SCALE_XCOORD(C, X, S) ( ( (X) * ((C)->width  - 1) ) / ( (C)->x_div * (S) ) )
263#define SCALE_YCOORD(C, Y, S) ( ( (Y) * ((C)->height - 1) ) / ( (C)->y_div * (S) ) )
264#define TRANSLATE_XCOORD(C, X, S) SCALE_XCOORD((C), (X) - (C)->x_off * (S), (S) )
265#define TRANSLATE_YCOORD(C, Y, S) SCALE_YCOORD((C), (Y) - (C)->y_off * (S), (S) )
266
267#define READ_PIXEL(C, X, Y) read_pixel((C)->graphic, (X), (Y))
268#define DRAW_PIXEL(C, X, Y, COL) draw_solid_pixel((C)->graphic, (X), (Y), (COL))
269#define DRAW_ALL(C, COL) \
270    draw_solid_rectangle((C)->graphic, 0, 0, (C)->width, (C)->height, (COL))
271
272static unsigned get_shade_character_pixel(unsigned char const *pixels,
273					  unsigned w, unsigned h,
274					  unsigned smaxf, unsigned scale,
275					  int slant_dx, int px, int py);
276static void get_bitmap_of_character(RegisGraphicsContext const *context,
277				    char ch, unsigned maxw, unsigned maxh,
278				    unsigned char *pixels,
279				    unsigned *w, unsigned *h,
280				    unsigned max_pixels);
281
282static int
283ifloor(double d)
284{
285    double dl = floor(d);
286    return (int) dl;
287}
288
289static int
290isqrt(double d)
291{
292    double dl = sqrt(d);
293    return (int) dl;
294}
295
296static void
297draw_regis_pixel(RegisGraphicsContext *context, int x, int y,
298		 unsigned value)
299{
300    unsigned color = 0;
301
302    switch (context->temporary_write_controls.write_style) {
303    case WRITE_STYLE_OVERLAY:
304	/*
305	 * Update pixels with foreground when pattern is 1,
306	 * don't change when pattern is 0.
307	 */
308	if (!value) {
309	    return;
310	}
311
312	if (context->temporary_write_controls.invert_pattern) {
313	    color = context->background;
314	} else {
315	    color = context->temporary_write_controls.foreground;
316	}
317	break;
318
319    case WRITE_STYLE_REPLACE:
320	/*
321	 * Update pixels with foreground when pattern is 1,
322	 * set to background when pattern is 0.
323	 */
324	{
325	    unsigned fg, bg;
326
327	    if (context->temporary_write_controls.invert_pattern) {
328		fg = context->background;
329		bg = context->temporary_write_controls.foreground;
330	    } else {
331		fg = context->temporary_write_controls.foreground;
332		bg = context->background;
333	    }
334	    color = value ? fg : bg;
335	}
336	break;
337
338    case WRITE_STYLE_COMPLEMENT:
339	/*
340	 * Update pixels with background when pattern is 1,
341	 * don't change when pattern is 0.
342	 */
343	if (!value) {
344	    return;
345	}
346
347	color = READ_PIXEL(context, x, y);
348	if (color == COLOR_HOLE)
349	    color = context->background;
350	color = color ^ context->all_planes;
351	break;
352
353    case WRITE_STYLE_ERASE:
354	/* Update pixels to foreground. */
355	if (context->temporary_write_controls.invert_pattern) {
356	    color = context->temporary_write_controls.foreground;
357	} else {
358	    color = context->background;
359	}
360	break;
361    }
362
363    DRAW_PIXEL(context, x, y, color);
364}
365
366static void
367shade_pattern_to_pixel(RegisGraphicsContext *context, unsigned dim, int ref,
368		       int x, int y)
369{
370    unsigned value;
371
372    if (dim == WRITE_SHADING_REF_X) {
373	int delta = x > ref ? 1 : -1;
374	int curr_x;
375
376	context->pattern_bit = 1U << (((unsigned) y) & 7U);
377	for (curr_x = ref; curr_x != x + delta; curr_x += delta) {
378	    value = context->temporary_write_controls.pattern &
379		context->pattern_bit;
380	    draw_regis_pixel(context, curr_x, y, value);
381	}
382    } else {
383	int delta = y > ref ? 1 : -1;
384	int curr_y;
385
386	for (curr_y = ref; curr_y != y + delta; curr_y += delta) {
387	    context->pattern_bit = 1U << (((unsigned) curr_y) & 7U);
388	    value = context->temporary_write_controls.pattern &
389		context->pattern_bit;
390	    draw_regis_pixel(context, x, curr_y, value);
391	}
392    }
393}
394
395static void
396shade_char_to_pixel(RegisGraphicsContext *context, unsigned char const *pixels,
397		    unsigned w, unsigned h, unsigned dim, int ref, int x, int y)
398{
399    unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
400    unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
401    unsigned smaxf;
402    unsigned s;
403    unsigned scale;
404    unsigned value;
405
406    if (xmaxf > ymaxf) {
407	smaxf = ymaxf;
408	s = h;
409    } else {
410	smaxf = xmaxf;
411	s = w;
412    }
413    scale = (s << SCALE_FIXED_POINT) / smaxf;
414
415    if (dim == WRITE_SHADING_REF_X) {
416	int delta = x > ref ? 1 : -1;
417	int curr_x;
418
419	for (curr_x = ref; curr_x != x + delta; curr_x += delta) {
420	    value = get_shade_character_pixel(pixels, w, h, smaxf, scale, 0,
421					      curr_x, y);
422	    draw_regis_pixel(context, curr_x, y, value);
423	}
424    } else {
425	int delta = y > ref ? 1 : -1;
426	int curr_y;
427
428	for (curr_y = ref; curr_y != y + delta; curr_y += delta) {
429	    value = get_shade_character_pixel(pixels, w, h, smaxf, scale, 0, x,
430					      curr_y);
431	    draw_regis_pixel(context, x, curr_y, value);
432	}
433    }
434}
435
436static void
437draw_patterned_pixel(RegisGraphicsContext *context, int x, int y)
438{
439    if (context->pattern_count >=
440	context->temporary_write_controls.pattern_multiplier) {
441	context->pattern_count = 0U;
442	context->pattern_bit = ROT_LEFT(context->pattern_bit);
443    }
444    context->pattern_count++;
445
446    draw_regis_pixel(context, x, y,
447		     context->temporary_write_controls.pattern &
448		     context->pattern_bit);
449}
450
451static void
452shade_to_pixel(RegisGraphicsContext *context, unsigned dim, int ref,
453	       int x, int y)
454{
455    if (context->temporary_write_controls.shading_character != '\0') {
456	unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
457	unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
458	char ch = context->temporary_write_controls.shading_character;
459	unsigned char pixels[MAX_GLYPH_PIXELS];
460	unsigned w, h;
461
462	get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h,
463				MAX_GLYPH_PIXELS);
464	if (w > 0 && h > 0) {
465	    shade_char_to_pixel(context, pixels, w, h, dim, ref, x, y);
466	}
467    } else {
468	shade_pattern_to_pixel(context, dim, ref, x, y);
469    }
470}
471
472static void
473draw_or_save_patterned_pixel(RegisGraphicsContext *context, int x, int y)
474{
475    if (context->fill_mode == 1) {
476	if (context->fill_point_count >= MAX_FILL_POINTS) {
477	    TRACE(("point %d,%d can not be added to filled polygon\n",
478		   x, y));
479	    return;
480	}
481	if (context->fill_point_count > 0U &&
482	    context->fill_points[context->fill_point_count - 1U].x == x &&
483	    context->fill_points[context->fill_point_count - 1U].y == y) {
484	    return;
485	}
486	context->fill_points[context->fill_point_count].x = x;
487	context->fill_points[context->fill_point_count].y = y;
488	context->fill_point_count++;
489	return;
490    }
491
492    if (context->temporary_write_controls.shading_enabled) {
493	unsigned dim = context->temporary_write_controls.shading_reference_dim;
494	int ref = context->temporary_write_controls.shading_reference;
495
496	shade_to_pixel(context, dim, ref, x, y);
497	return;
498    }
499
500    draw_patterned_pixel(context, x, y);
501}
502
503static int
504sort_points(void const *l, void const *r)
505{
506    RegisPoint const *const lp = l;
507    RegisPoint const *const rp = r;
508
509    if (lp->y < rp->y)
510	return -1;
511    if (lp->y > rp->y)
512	return +1;
513    if (lp->x < rp->x)
514	return -1;
515    if (lp->x > rp->x)
516	return +1;
517    return 0;
518}
519
520static void
521draw_filled_polygon(RegisGraphicsContext *context)
522{
523    unsigned p;
524    int new_x, new_y;
525    int old_x, old_y;
526    int inside;
527    unsigned char pixels[MAX_GLYPH_PIXELS];
528    unsigned w, h;
529
530    if (context->temporary_write_controls.shading_character != '\0') {
531	char ch = context->temporary_write_controls.shading_character;
532	unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
533	unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
534
535	get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h,
536				MAX_GLYPH_PIXELS);
537	if (w < 1U || h < 1U) {
538	    return;
539	}
540    }
541
542    qsort(context->fill_points, context->fill_point_count,
543	  sizeof(context->fill_points[0]), sort_points);
544
545    old_x = DUMMY_STACK_X;
546    old_y = DUMMY_STACK_Y;
547    inside = 0;
548    for (p = 0U; p < context->fill_point_count; p++) {
549	new_x = context->fill_points[p].x;
550	new_y = context->fill_points[p].y;
551#if 0
552	printf("got %d,%d (%d,%d) inside=%d\n", new_x, new_y, old_x, old_y, inside);
553#endif
554
555	/*
556	 * FIXME: This is using pixels to represent lines which loses
557	 * information about exact slope and how many lines are present which
558	 * causes misbehavior with some inputs (especially complex polygons).
559	 * It also takes more room than remembering vertices, but I'd rather
560	 * not have to implement line segments for arcs.  Maybe store a count
561	 * at each vertex instead (doesn't fix the slope problem).
562	 */
563	/*
564	 * FIXME: Change this to only draw inside of polygons, and round
565	 * points in a uniform direction to avoid overlapping drawing.  As an
566	 * option we could continue to support drawing the outline.
567	 */
568	if (new_y != old_y) {
569	    if (inside) {
570		/*
571		 * Just draw the vertical line when there is not a matching
572		 * edge on the right side.
573		 */
574		if (context->temporary_write_controls.shading_character != '\0') {
575		    shade_char_to_pixel(context, pixels, w, h,
576					WRITE_SHADING_REF_X,
577					old_x, old_x, old_y);
578		} else {
579		    shade_pattern_to_pixel(context, WRITE_SHADING_REF_X,
580					   old_x, old_x, old_y);
581		}
582	    }
583	    inside = 1;
584	} else {
585	    if (inside) {
586		if (context->temporary_write_controls.shading_character != '\0') {
587		    shade_char_to_pixel(context, pixels, w, h,
588					WRITE_SHADING_REF_X,
589					old_x, new_x, new_y);
590		} else {
591		    shade_pattern_to_pixel(context, WRITE_SHADING_REF_X,
592					   old_x, new_x, new_y);
593		}
594	    }
595	    if (new_x > old_x + 1) {
596		inside = !inside;
597	    }
598	}
599
600	old_x = new_x;
601	old_y = new_y;
602    }
603}
604
605static void
606draw_patterned_line(RegisGraphicsContext *context, int x1, int y1,
607		    int x2, int y2)
608{
609    int x, y;
610    int dx, dy;
611    int dir, diff;
612
613    dx = abs(x1 - x2);
614    dy = abs(y1 - y2);
615
616    if (dx > dy) {
617	if (x1 > x2) {
618	    int tmp;
619	    EXCHANGE(x1, x2, tmp);
620	    EXCHANGE(y1, y2, tmp);
621	}
622	if (y1 < y2)
623	    dir = 1;
624	else if (y1 > y2)
625	    dir = -1;
626	else
627	    dir = 0;
628
629	diff = 0;
630	y = y1;
631	for (x = x1; x <= x2; x++) {
632	    if (diff >= dx) {
633		diff -= dx;
634		y += dir;
635	    }
636	    diff += dy;
637	    draw_or_save_patterned_pixel(context, x, y);
638	}
639    } else {
640	if (y1 > y2) {
641	    int tmp;
642	    EXCHANGE(y1, y2, tmp);
643	    EXCHANGE(x1, x2, tmp);
644	}
645	if (x1 < x2)
646	    dir = 1;
647	else if (x1 > x2)
648	    dir = -1;
649	else
650	    dir = 0;
651
652	diff = 0;
653	x = x1;
654	for (y = y1; y <= y2; y++) {
655	    if (diff >= dy) {
656		diff -= dy;
657		x += dir;
658	    }
659	    diff += dx;
660	    draw_or_save_patterned_pixel(context, x, y);
661	}
662    }
663}
664
665typedef struct {
666    int dxx;
667    int dxy;
668    int dyx;
669    int dyy;
670} quadmap_coords;
671
672static void
673draw_patterned_arc(RegisGraphicsContext *context,
674		   int cx, int cy,
675		   int ex, int ey,
676		   int a_start, int a_length,
677		   int *ex_final, int *ey_final)
678{
679    const double third = hypot((double) (cx - ex), (double) (cy - ey));
680    const int radius = (int) third;
681    const int ra = radius;
682    const int rb = radius;
683    const quadmap_coords neg_quadmap[4] =
684    {
685	{-1, 0, 0, +1},
686	{0, -1, -1, 0},
687	{+1, 0, 0, -1},
688	{0, +1, +1, 0},
689    };
690    const quadmap_coords pos_quadmap[4] =
691    {
692	{-1, 0, 0, -1},
693	{0, -1, +1, 0},
694	{+1, 0, 0, +1},
695	{0, +1, -1, 0},
696    };
697    const quadmap_coords *quadmap;
698    int total_points;
699    int points_start, points_stop;
700    int points;
701    unsigned iterations;
702    int quad;
703    long rx, ry;
704    long dx, dy;
705    int x, y;
706    long e2;
707    long error;
708
709    TRACE(("a_length=%d a_start=%d\n", a_length, a_start));
710    if (a_length == 0)
711	return;
712    if (a_length > 0) {
713	quadmap = pos_quadmap;
714    } else {
715	quadmap = neg_quadmap;
716	if (a_start != 0)
717	    a_start = 360 - a_start;
718    }
719
720    rx = -ra;
721    ry = 0;
722    e2 = rb;
723    dx = (2 * rx + 1) * e2 * e2;
724    dy = rx * rx;
725    error = dx + dy;
726    total_points = 0;
727    do {
728	total_points += 4;
729	e2 = 2 * error;
730	if (e2 >= dx) {
731	    rx++;
732	    dx += 2 * rb * rb;
733	    error += dx;
734	}
735	if (e2 <= dy) {
736	    ry++;
737	    dy += 2 * ra * ra;
738	    error += dy;
739	}
740    }
741    while (rx <= 0);
742    points_start = (total_points * a_start) / 360;
743    points_stop = (total_points * a_start +
744		   total_points * abs(a_length) + 359) / 360;
745    TRACE(("drawing arc with %d points from %d angle for %d degrees (from point %d to %d out of %d)\n",
746	   total_points, a_start, a_length, points_start, points_stop,
747	   total_points));
748
749    points = 0;
750    for (iterations = 0U; iterations < 8U; iterations++) {
751	quad = iterations & 0x3;
752
753	rx = -ra;
754	ry = 0;
755	e2 = rb;
756	dx = (2 * rx + 1) * e2 * e2;
757	dy = rx * rx;
758	error = dx + dy;
759	do {
760	    if (points >= points_start && points <= points_stop) {
761		x = (int) (cx +
762			   quadmap[quad].dxx * rx +
763			   quadmap[quad].dxy * ry);
764		y = (int) (cy +
765			   quadmap[quad].dyx * rx +
766			   quadmap[quad].dyy * ry);
767		draw_or_save_patterned_pixel(context, x, y);
768		if (ex_final)
769		    *ex_final = x;
770		if (ey_final)
771		    *ey_final = y;
772	    }
773	    points++;
774
775	    e2 = 2 * error;
776	    if (e2 >= dx) {
777		rx++;
778		dx += 2 * rb * rb;
779		error += dx;
780	    }
781	    if (e2 <= dy) {
782		ry++;
783		dy += 2 * ra * ra;
784		error += dy;
785	    }
786	}
787	while (rx <= 0);
788    }
789}
790
791/*
792 * The plot* functions are based on optimized rasterization primitves written by Zingl Alois.
793 * See http://members.chello.at/easyfilter/bresenham.html
794 */
795
796/*
797 * FIXME:
798 * This is a terrible temporary hack.  The plot functions below can be adapted
799 * to work like the other rasterization functions but there's no point in doing
800 * that until we know we don't have to write something completely different.
801 */
802static RegisGraphicsContext *global_context;
803static void
804setPixel(int x, int y)
805{
806    draw_or_save_patterned_pixel(global_context, x, y);
807}
808
809static void
810plotLine(int x0, int y0, int x1, int y1)
811{
812    int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
813    int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
814    int err = dx + dy, e2;	/* error value e_xy */
815
816    for (;;) {			/* loop */
817	setPixel(x0, y0);
818	e2 = 2 * err;
819	if (e2 >= dy) {		/* e_xy+e_x > 0 */
820	    if (x0 == x1)
821		break;
822	    err += dy;
823	    x0 += sx;
824	}
825	if (e2 <= dx) {		/* e_xy+e_y < 0 */
826	    if (y0 == y1)
827		break;
828	    err += dx;
829	    y0 += sy;
830	}
831    }
832}
833
834static void
835plotQuadBezierSeg(int x0, int y0, int x1, int y1, int x2, int y2)
836{				/* plot a limited quadratic Bezier segment */
837    int sx = x2 - x1;
838    int sy = y2 - y1;
839    long xx = (x0 - x1);	/* relative values for checks */
840    long yy = (y0 - y1);
841    double cur = (double) (xx * sy - yy * sx);	/* curvature */
842
843    assert(xx * sx <= 0 && yy * sy <= 0);	/* sign of gradient must not change */
844
845    if (sx * (long) sx + sy * (long) sy > xx * xx + yy * yy) {	/* begin with longer part */
846	x2 = x0;
847	x0 = sx + x1;
848	y2 = y0;
849	y0 = sy + y1;
850	cur = -cur;		/* swap P0 P2 */
851    }
852    if (cur != 0) {		/* no straight line */
853	long xy;
854	double dx, dy, err;
855
856	xx += sx;
857	xx *= sx = x0 < x2 ? 1 : -1;	/* x step direction */
858	yy += sy;
859	yy *= sy = y0 < y2 ? 1 : -1;	/* y step direction */
860	xy = 2 * xx * yy;
861	xx *= xx;
862	yy *= yy;		/* differences 2nd degree */
863	if (cur * sx * sy < 0) {	/* negated curvature? */
864	    xx = -xx;
865	    yy = -yy;
866	    xy = -xy;
867	    cur = -cur;
868	}
869	/* differences 1st degree */
870	dx = ((4.0 * sy * cur * (x1 - x0)) + (double) xx) - (double) xy;
871	dy = ((4.0 * sx * cur * (y0 - y1)) + (double) yy) - (double) xy;
872	xx += xx;
873	yy += yy;
874	err = dx + dy + (double) xy;	/* error 1st step */
875	do {
876	    setPixel(x0, y0);	/* plot curve */
877	    if (x0 == x2 && y0 == y2)
878		return;		/* last pixel -> curve finished */
879	    y1 = (2 * err) < dx;	/* save value for test of y step */
880	    if ((2 * err) > dy) {
881		x0 += sx;
882		dx -= (double) xy;
883		dy += (double) yy;
884		err += dy;
885	    }			/* x step */
886	    if (y1) {
887		y0 += sy;
888		dy -= (double) xy;
889		dx += (double) xx;
890		err += dx;
891	    }			/* y step */
892	} while (dy < 0 && dx > 0);	/* gradient negates -> algorithm fails */
893    }
894    plotLine(x0, y0, x2, y2);	/* plot remaining part to end */
895}
896
897#if 0
898static void
899plotQuadBezier(int x0, int y0, int x1, int y1, int x2, int y2)
900{				/* plot any quadratic Bezier curve */
901    int x = x0 - x1;
902    int y = y0 - y1;
903    double t = x0 - 2 * x1 + x2;
904    double r;
905
906    if ((long) x * (x2 - x1) > 0) {	/* horizontal cut at P4? */
907	if ((long) y * (y2 - y1) > 0)	/* vertical cut at P6 too? */
908	    if (fabs((y0 - 2 * y1 + y2) / t * x) > abs(y)) {	/* which first? */
909		x0 = x2;
910		x2 = x + x1;
911		y0 = y2;
912		y2 = y + y1;	/* swap points */
913	    }			/* now horizontal cut at P4 comes first */
914	t = (x0 - x1) / t;
915	r = (1 - t) * ((1 - t) * y0 + 2.0 * t * y1) + t * t * y2;	/* By(t=P4) */
916	t = (x0 * x2 - x1 * x1) * t / (x0 - x1);	/* gradient dP4/dx=0 */
917	x = ifloor(t + 0.5);
918	y = ifloor(r + 0.5);
919	r = (y1 - y0) * (t - x0) / (x1 - x0) + y0;	/* intersect P3 | P0 P1 */
920	plotQuadBezierSeg(x0, y0, x, ifloor(r + 0.5), x, y);
921	r = (y1 - y2) * (t - x2) / (x1 - x2) + y2;	/* intersect P4 | P1 P2 */
922	x0 = x1 = x;
923	y0 = y;
924	y1 = ifloor(r + 0.5);	/* P0 = P4, P1 = P8 */
925    }
926    if ((long) (y0 - y1) * (y2 - y1) > 0) {	/* vertical cut at P6? */
927	t = y0 - 2 * y1 + y2;
928	t = (y0 - y1) / t;
929	r = (1 - t) * ((1 - t) * x0 + 2.0 * t * x1) + t * t * x2;	/* Bx(t=P6) */
930	t = (y0 * y2 - y1 * y1) * t / (y0 - y1);	/* gradient dP6/dy=0 */
931	x = ifloor(r + 0.5);
932	y = ifloor(t + 0.5);
933	r = (x1 - x0) * (t - y0) / (y1 - y0) + x0;	/* intersect P6 | P0 P1 */
934	plotQuadBezierSeg(x0, y0, ifloor(r + 0.5), y, x, y);
935	r = (x1 - x2) * (t - y2) / (y1 - y2) + x2;	/* intersect P7 | P1 P2 */
936	x0 = x;
937	x1 = ifloor(r + 0.5);
938	y0 = y1 = y;		/* P0 = P6, P1 = P7 */
939    }
940    plotQuadBezierSeg(x0, y0, x1, y1, x2, y2);	/* remaining part */
941}
942#endif
943
944static void
945plotCubicBezierSeg(int x0, int y0,
946		   double x1, double y1,
947		   double x2, double y2,
948		   int x3, int y3)
949{				/* plot limited cubic Bezier segment */
950    int f, fx, fy, tt;
951    int leg = 1;
952    int sx = x0 < x3 ? 1 : -1;
953    int sy = y0 < y3 ? 1 : -1;	/* step direction */
954    double xc = -fabs(x0 + x1 - x2 - x3);
955    double xa = xc - 4 * sx * (x1 - x2);
956    double xb = sx * (x0 - x1 - x2 + x3);
957    double yc = -fabs(y0 + y1 - y2 - y3);
958    double ya = yc - 4 * sy * (y1 - y2);
959    double yb = sy * (y0 - y1 - y2 + y3);
960    double ab, ac, bc, cb, xx, xy, yy, dx, dy, ex, *pxy;
961    double EP = 0.01;
962    /* check for curve restrains */
963    /* slope P0-P1 == P2-P3    and  (P0-P3 == P1-P2      or   no slope change) */
964    assert((x1 - x0) * (x2 - x3) < EP &&
965	   ((x3 - x0) * (x1 - x2) < EP || xb * xb < xa * xc + EP));
966    assert((y1 - y0) * (y2 - y3) < EP &&
967	   ((y3 - y0) * (y1 - y2) < EP || yb * yb < ya * yc + EP));
968
969    if (xa == 0 && ya == 0) {	/* quadratic Bezier */
970	sx = ifloor((3 * x1 - x0 + 1) / 2);
971	sy = ifloor((3 * y1 - y0 + 1) / 2);	/* new midpoint */
972	plotQuadBezierSeg(x0, y0, sx, sy, x3, y3);
973	return;
974    }
975    x1 = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) + 1;	/* line lengths */
976    x2 = (x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3) + 1;
977    do {			/* loop over both ends */
978	ab = xa * yb - xb * ya;
979	ac = xa * yc - xc * ya;
980	bc = xb * yc - xc * yb;
981	ex = ab * (ab + ac - 3 * bc) + ac * ac;		/* P0 part of self-intersection loop? */
982	f = ((ex > 0.0)
983	     ? 1
984	     : isqrt(1 + 1024 / x1));	/* calculate resolution */
985	ab *= f;
986	ac *= f;
987	bc *= f;
988	ex *= f * f;		/* increase resolution */
989	xy = 9 * (ab + ac + bc) / 8;
990	cb = 8 * (xa - ya);	/* init differences of 1st degree */
991	dx = 27 * (8 * ab * (yb * yb - ya * yc) +
992		   ex * (ya + 2 * yb + yc)) / 64 - ya * ya * (xy - ya);
993	dy = 27 * (8 * ab * (xb * xb - xa * xc) -
994		   ex * (xa + 2 * xb + xc)) / 64 - xa * xa * (xy + xa);
995	/* init differences of 2nd degree */
996	xx = 3 * (3 * ab * (3 * yb * yb - ya * ya - 2 * ya * yc) -
997		  ya * (3 * ac * (ya + yb) + ya * cb)) / 4;
998	yy = 3 * (3 * ab * (3 * xb * xb - xa * xa - 2 * xa * xc) -
999		  xa * (3 * ac * (xa + xb) + xa * cb)) / 4;
1000	xy = xa * ya * (6 * ab + 6 * ac - 3 * bc + cb);
1001	ac = ya * ya;
1002	cb = xa * xa;
1003	xy = 3 * (xy + 9 * f * (cb * yb * yc - xb * xc * ac) -
1004		  18 * xb * yb * ab) / 8;
1005
1006	if (ex < 0) {		/* negate values if inside self-intersection loop */
1007	    dx = -dx;
1008	    dy = -dy;
1009	    xx = -xx;
1010	    yy = -yy;
1011	    xy = -xy;
1012	    ac = -ac;
1013	    cb = -cb;
1014	}			/* init differences of 3rd degree */
1015	ab = 6 * ya * ac;
1016	ac = -6 * xa * ac;
1017	bc = 6 * ya * cb;
1018	cb = -6 * xa * cb;
1019	dx += xy;
1020	ex = dx + dy;
1021	dy += xy;		/* error of 1st step */
1022
1023	for (pxy = &xy, fx = fy = f; x0 != x3 && y0 != y3;) {
1024	    setPixel(x0, y0);	/* plot curve */
1025	    do {		/* move sub-steps of one pixel */
1026		if (dx > *pxy || dy < *pxy)
1027		    goto exit;	/* confusing values */
1028		y1 = 2 * ex - dy;	/* save value for test of y step */
1029		if (2 * ex >= dx) {	/* x sub-step */
1030		    fx--;
1031		    ex += dx += xx;
1032		    dy += xy += ac;
1033		    yy += bc;
1034		    xx += ab;
1035		}
1036		if (y1 <= 0) {	/* y sub-step */
1037		    fy--;
1038		    ex += dy += yy;
1039		    dx += xy += bc;
1040		    xx += ac;
1041		    yy += cb;
1042		}
1043	    } while (fx > 0 && fy > 0);		/* pixel complete? */
1044	    if (2 * fx <= f) {
1045		x0 += sx;
1046		fx += f;
1047	    }			/* x step */
1048	    if (2 * fy <= f) {
1049		y0 += sy;
1050		fy += f;
1051	    }			/* y step */
1052	    if (pxy == &xy && dx < 0 && dy > 0)
1053		pxy = &EP;	/* pixel ahead valid */
1054	}
1055      exit:
1056	EXCHANGE(x0, x3, tt);
1057	sx = -sx;
1058	xb = -xb;		/* swap legs */
1059	EXCHANGE(y0, y3, tt);
1060	sy = -sy;
1061	yb = -yb;
1062	x1 = x2;
1063    } while (leg--);		/* try other end */
1064    plotLine(x0, y0, x3, y3);	/* remaining part in case of cusp or crunode */
1065}
1066
1067static void
1068plotCubicBezier(int x0, int y0, int x1, int y1,
1069		int x2, int y2, int x3, int y3)
1070{				/* plot any cubic Bezier curve */
1071    int n = 0, i = 0;
1072    long xc = x0 + x1 - x2 - x3;
1073    long xa = xc - 4 * (x1 - x2);
1074    long xb = x0 - x1 - x2 + x3;
1075    long xd = xb + 4 * (x1 + x2);
1076    long yc = y0 + y1 - y2 - y3;
1077    long ya = yc - 4 * (y1 - y2);
1078    long yb = y0 - y1 - y2 + y3;
1079    long yd = yb + 4 * (y1 + y2);
1080    double fx0 = x0, fx1, fx2, fx3, fy0 = y0, fy1, fy2, fy3;
1081    double t1 = (double) (xb * xb - xa * xc), t2, t[5];
1082
1083#ifdef DEBUG_BEZIER
1084    printf("plotCubicBezier(%d,%d, %d,%d, %d,%d, %d,%d\n",
1085	   x0, y0, x1, y1, x2, y2, x3, y3);
1086#endif
1087    /* sub-divide curve at gradient sign changes */
1088    if (xa == 0) {		/* horizontal */
1089	if (labs(xc) < 2 * labs(xb))
1090	    t[n++] = (double) xc / (2.0 * (double) xb);		/* one change */
1091    } else if (t1 > 0.0) {	/* two changes */
1092	t2 = sqrt(t1);
1093	t1 = ((double) xb - t2) / (double) xa;
1094	if (fabs(t1) < 1.0)
1095	    t[n++] = t1;
1096	t1 = ((double) xb + t2) / (double) xa;
1097	if (fabs(t1) < 1.0)
1098	    t[n++] = t1;
1099    }
1100    t1 = (double) (yb * yb - ya * yc);
1101    if (ya == 0) {		/* vertical */
1102	if (labs(yc) < 2 * labs(yb))
1103	    t[n++] = (double) yc / (2.0 * (double) yb);		/* one change */
1104    } else if (t1 > 0.0) {	/* two changes */
1105	t2 = sqrt(t1);
1106	t1 = ((double) yb - t2) / (double) ya;
1107	if (fabs(t1) < 1.0)
1108	    t[n++] = t1;
1109	t1 = ((double) yb + t2) / (double) ya;
1110	if (fabs(t1) < 1.0)
1111	    t[n++] = t1;
1112    }
1113    for (i = 1; i < n; i++)	/* bubble sort of 4 points */
1114	if ((t1 = t[i - 1]) > t[i]) {
1115	    t[i - 1] = t[i];
1116	    t[i] = t1;
1117	    i = 0;
1118	}
1119
1120    t1 = -1.0;
1121    t[n] = 1.0;			/* begin / end point */
1122    for (i = 0; i <= n; i++) {	/* plot each segment separately */
1123	t2 = t[i];		/* sub-divide at t[i-1], t[i] */
1124	fx1 = (t1 * (t1 * (double) xb - (double) (2 * xc)) -
1125	       t2 * (t1 * (t1 * (double) xa - (double) (2 * xb)) + (double)
1126		     xc) + (double) xd) / 8 - fx0;
1127	fy1 = (t1 * (t1 * (double) yb - (double) (2 * yc)) -
1128	       t2 * (t1 * (t1 * (double) ya - (double) (2 * yb)) + (double)
1129		     yc) + (double) yd) / 8 - fy0;
1130	fx2 = (t2 * (t2 * (double) xb - (double) (2 * xc)) -
1131	       t1 * (t2 * (t2 * (double) xa - (double) (2 * xb)) + (double)
1132		     xc) + (double) xd) / 8 - fx0;
1133	fy2 = (t2 * (t2 * (double) yb - (double) (2 * yc)) -
1134	       t1 * (t2 * (t2 * (double) ya - (double) (2 * yb)) + (double)
1135		     yc) + (double) yd) / 8 - fy0;
1136	fx0 -= fx3 = (t2 * (t2 * ((double) (3 * xb) - t2 * (double) xa) -
1137			    (double) (3 * xc)) + (double) xd) / 8;
1138	fy0 -= fy3 = (t2 * (t2 * ((double) (3 * yb) - t2 * (double) ya) -
1139			    (double) (3 * yc)) + (double) yd) / 8;
1140	x3 = ifloor(fx3 + 0.5);
1141	y3 = ifloor(fy3 + 0.5);	/* scale bounds to int */
1142	if (fx0 != 0.0) {
1143	    fx1 *= fx0 = (x0 - x3) / fx0;
1144	    fx2 *= fx0;
1145	}
1146	if (fy0 != 0.0) {
1147	    fy1 *= fy0 = (y0 - y3) / fy0;
1148	    fy2 *= fy0;
1149	}
1150	if (x0 != x3 || y0 != y3)	/* segment t1 - t2 */
1151	    plotCubicBezierSeg(x0, y0,
1152			       x0 + fx1, y0 + fy1,
1153			       x0 + fx2, y0 + fy2,
1154			       x3, y3);
1155	x0 = x3;
1156	y0 = y3;
1157	fx0 = fx3;
1158	fy0 = fy3;
1159	t1 = t2;
1160    }
1161}
1162
1163#if 0
1164static void
1165plotQuadSpline(int n, int x[], int y[], int skip_segments)
1166{				/* plot quadratic spline, destroys input arrays x,y */
1167#define M_MAX 12
1168    double mi = 1, m[M_MAX];	/* diagonal constants of matrix */
1169    int i, x0, y0, x1, y1, x2, y2;
1170#ifdef DEBUG_SPLINE_SEGMENTS
1171    int color = 0;
1172#endif
1173
1174    assert(n > 1);		/* need at least 3 points P[0]..P[n] */
1175
1176#ifdef DEBUG_SPLINE_POINTS
1177    {
1178	int save_pattern;
1179
1180	i = 0;
1181	global_context->temporary_write_controls.foreground = 11;
1182	save_pattern = global_context->temporary_write_controls.pattern;
1183	global_context->temporary_write_controls.pattern = 0xff;
1184	draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0,
1185			   360, NULL, NULL);
1186	i++;
1187	global_context->temporary_write_controls.foreground = 15;
1188	for (; i < n; i++) {
1189	    draw_patterned_arc(global_context,
1190			       x[i], y[i],
1191			       x[i] + 2, y[i],
1192			       0, 360, NULL, NULL);
1193	}
1194	global_context->temporary_write_controls.foreground = 10;
1195	draw_patterned_arc(global_context, x[i], y[n], x[i] + 2, y[i], 0,
1196			   360, NULL, NULL);
1197	global_context->temporary_write_controls.pattern = save_pattern;
1198    }
1199#endif
1200
1201    x2 = x[n];
1202    y2 = y[n];
1203
1204    x[1] = x0 = 8 * x[1] - 2 * x[0];	/* first row of matrix */
1205    y[1] = y0 = 8 * y[1] - 2 * y[0];
1206
1207    for (i = 2; i < n; i++) {	/* forward sweep */
1208	if (i - 2 < M_MAX)
1209	    m[i - 2] = mi = 1.0 / (6.0 - mi);
1210	x[i] = x0 = ifloor(8 * x[i] - x0 * mi + 0.5);	/* store yi */
1211	y[i] = y0 = ifloor(8 * y[i] - y0 * mi + 0.5);
1212    }
1213    x1 = ifloor((x0 - 2 * x2) / (5.0 - mi) + 0.5);	/* correction last row */
1214    y1 = ifloor((y0 - 2 * y2) / (5.0 - mi) + 0.5);
1215
1216    for (i = n - 2; i > 0; i--) {	/* back substitution */
1217	if (i <= M_MAX)
1218	    mi = m[i - 1];
1219	x0 = ifloor((x[i] - x1) * mi + 0.5);	/* next corner */
1220	y0 = ifloor((y[i] - y1) * mi + 0.5);
1221#ifdef DEBUG_SPLINE_SEGMENTS
1222	color++;
1223	global_context->temporary_write_controls.foreground = color;
1224#endif
1225	if ((n - 2) - i < skip_segments)
1226	    plotQuadBezier((x0 + x1) / 2, (y0 + y1) / 2, x1, y1, x2, y2);
1227	x2 = (x0 + x1) / 2;
1228	x1 = x0;
1229	y2 = (y0 + y1) / 2;
1230	y1 = y0;
1231    }
1232#ifdef DEBUG_SPLINE_SEGMENTS
1233    color++;
1234    global_context->temporary_write_controls.foreground = color;
1235#endif
1236    if (skip_segments > 0)
1237	plotQuadBezier(x[0], y[0], x1, y1, x2, y2);
1238}
1239#endif
1240
1241static void
1242plotCubicSpline(int n, int x[], int y[], int skip_first_last)
1243{
1244#define M_MAX 12
1245    double mi = 0.25, m[M_MAX];	/* diagonal constants of matrix */
1246    int x3, y3, x4, y4;
1247    int i, x0, y0, x1, y1, x2, y2;
1248#ifdef DEBUG_SPLINE_SEGMENTS
1249    RegisterNum color = 0;
1250#endif
1251
1252    assert(n > 2);		/* need at least 4 points P[0]..P[n] */
1253
1254#ifdef DEBUG_SPLINE_POINTS
1255    {
1256	unsigned save_pattern;
1257
1258	i = 0;
1259	global_context->temporary_write_controls.foreground = 11;
1260	save_pattern = global_context->temporary_write_controls.pattern;
1261	global_context->temporary_write_controls.pattern = 0xff;
1262	draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0,
1263			   360, NULL, NULL);
1264	i++;
1265	global_context->temporary_write_controls.foreground = 15;
1266	for (; i < n; i++) {
1267	    draw_patterned_arc(global_context,
1268			       x[i], y[i],
1269			       x[i] + 2, y[i],
1270			       0, 360, NULL, NULL);
1271	}
1272	global_context->temporary_write_controls.foreground = 10;
1273	draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0,
1274			   360, NULL, NULL);
1275	global_context->temporary_write_controls.pattern = save_pattern;
1276    }
1277#endif
1278
1279    x3 = x[n - 1];
1280    y3 = y[n - 1];
1281    x4 = x[n];
1282    y4 = y[n];
1283
1284    x[1] = x0 = 12 * x[1] - 3 * x[0];	/* first row of matrix */
1285    y[1] = y0 = 12 * y[1] - 3 * y[0];
1286
1287    for (i = 2; i < n; i++) {	/* forward sweep */
1288	if (i - 2 < M_MAX)
1289	    m[i - 2] = mi = 0.25 / (2.0 - mi);
1290	x[i] = x0 = ifloor(12 * x[i] - 2 * x0 * mi + 0.5);
1291	y[i] = y0 = ifloor(12 * y[i] - 2 * y0 * mi + 0.5);
1292    }
1293    x2 = ifloor((x0 - 3 * x4) / (7 - 4 * mi) + 0.5);	/* correct last row */
1294    /* printf("y0=%d, y4=%d mi=%g\n", y0, y4, mi); */
1295    y2 = ifloor((y0 - 3 * y4) / (7 - 4 * mi) + 0.5);
1296    /* printf("y2=%d, y3=%d, y4=%d\n", y2, y3, y4); */
1297#ifdef DEBUG_SPLINE_SEGMENTS
1298    color++;
1299    global_context->temporary_write_controls.foreground = color;
1300#endif
1301    if (!skip_first_last)
1302	plotCubicBezier(x3, y3, (x2 + x4) / 2, (y2 + y4) / 2, x4, y4, x4, y4);
1303
1304    if (n - 3 < M_MAX)
1305	mi = m[n - 3];
1306    x1 = ifloor((x[n - 2] - 2 * x2) * mi + 0.5);
1307    y1 = ifloor((y[n - 2] - 2 * y2) * mi + 0.5);
1308    for (i = n - 3; i > 0; i--) {	/* back substitution */
1309	if (i <= M_MAX)
1310	    mi = m[i - 1];
1311	x0 = ifloor((x[i] - 2 * x1) * mi + 0.5);
1312	y0 = ifloor((y[i] - 2 * y1) * mi + 0.5);
1313	x4 = ifloor((x0 + 4 * x1 + x2 + 3) / 6.0);	/* reconstruct P[i] */
1314	y4 = ifloor((y0 + 4 * y1 + y2 + 3) / 6.0);
1315#ifdef DEBUG_SPLINE_SEGMENTS
1316	color++;
1317	global_context->temporary_write_controls.foreground = color;
1318#endif
1319	plotCubicBezier(x4, y4,
1320			ifloor((2 * x1 + x2) / 3 + 0.5),
1321			ifloor((2 * y1 + y2) / 3 + 0.5),
1322			ifloor((x1 + 2 * x2) / 3 + 0.5),
1323			ifloor((y1 + 2 * y2) / 3 + 0.5),
1324			x3, y3);
1325	x3 = x4;
1326	y3 = y4;
1327	x2 = x1;
1328	y2 = y1;
1329	x1 = x0;
1330	y1 = y0;
1331    }
1332    x0 = x[0];
1333    x4 = ifloor((3 * x0 + 7 * x1 + 2 * x2 + 6) / 12.0);		/* reconstruct P[1] */
1334    y0 = y[0];
1335    y4 = ifloor((3 * y0 + 7 * y1 + 2 * y2 + 6) / 12.0);
1336#ifdef DEBUG_SPLINE_SEGMENTS
1337    global_context->temporary_write_controls.foreground = 4;
1338#endif
1339    plotCubicBezier(x4, y4,
1340		    ifloor((2 * x1 + x2) / 3 + 0.5),
1341		    ifloor((2 * y1 + y2) / 3 + 0.5),
1342		    ifloor((x1 + 2 * x2) / 3 + 0.5),
1343		    ifloor((y1 + 2 * y2) / 3 + 0.5),
1344		    x3, y3);
1345#ifdef DEBUG_SPLINE_SEGMENTS
1346    color++;
1347    global_context->temporary_write_controls.foreground = color;
1348#endif
1349    if (!skip_first_last)
1350	plotCubicBezier(x0, y0, x0, y0, (x0 + x1) / 2, (y0 + y1) / 2, x4, y4);
1351}
1352
1353static unsigned
1354find_free_alphabet_index(RegisGraphicsContext *context, unsigned alphabet,
1355			 unsigned pixw, unsigned pixh)
1356{
1357    unsigned ii, jj;
1358
1359    /* try an exact match */
1360    for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
1361	if (context->alphabets[ii].alphabet_num == alphabet &&
1362	    context->alphabets[ii].pixw == pixw &&
1363	    context->alphabets[ii].pixh == pixh) {
1364	    return ii;
1365	}
1366    }
1367
1368    /* otherwise use any empty slot */
1369    for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
1370	if (context->alphabets[ii].alphabet_num == INVALID_ALPHABET_NUM) {
1371	    context->alphabets[ii].alphabet_num = alphabet;
1372	    context->alphabets[ii].pixw = pixw;
1373	    context->alphabets[ii].pixh = pixh;
1374	    return ii;
1375	}
1376    }
1377
1378    /* otherwise recycle a slot with a different font size */
1379    for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
1380	if (context->alphabets[ii].alphabet_num == alphabet) {
1381	    context->alphabets[ii].pixw = pixw;
1382	    context->alphabets[ii].pixh = pixh;
1383	    context->alphabets[ii].name[0] = '\0';
1384	    context->alphabets[ii].fontname[0] = '\0';
1385	    context->alphabets[ii].use_font = 0;
1386	    if (context->alphabets[ii].bytes != NULL) {
1387		free(context->alphabets[ii].bytes);
1388		context->alphabets[ii].bytes = NULL;
1389	    }
1390	    for (jj = 0U; jj < MAX_GLYPHS; jj++) {
1391		context->alphabets[ii].loaded[jj] = 0;
1392	    }
1393	    return ii;
1394	}
1395    }
1396
1397    /* finally just recycle this arbitrary slot */
1398    context->alphabets[0U].alphabet_num = alphabet;
1399    context->alphabets[0U].pixw = pixw;
1400    context->alphabets[0U].pixh = pixh;
1401    context->alphabets[0U].name[0] = '\0';
1402    context->alphabets[0U].fontname[0] = '\0';
1403    context->alphabets[0U].use_font = 0;
1404    if (context->alphabets[0U].bytes != NULL) {
1405	free(context->alphabets[0U].bytes);
1406	context->alphabets[0U].bytes = NULL;
1407    }
1408    for (jj = 0U; jj < MAX_GLYPHS; jj++) {
1409	context->alphabets[0U].loaded[jj] = 0;
1410    }
1411
1412    return 0U;
1413}
1414
1415#ifdef DEBUG_SPECIFIC_CHAR_METRICS
1416static void
1417dump_bitmap_pixels(unsigned char const *pixels, unsigned w, unsigned h)
1418{
1419    unsigned yy, xx;
1420
1421    for (yy = 0U; yy < h; yy++) {
1422	printf(" ");
1423	for (xx = 0U; xx < w; xx++) {
1424	    if (pixels[yy * w + xx]) {
1425		printf("#");
1426	    } else {
1427		printf("_");
1428	    }
1429	}
1430	printf("\n");
1431    }
1432}
1433#endif
1434
1435#if OPT_RENDERFONT && defined(HAVE_TYPE_FCCHAR32)
1436static int
1437copy_bitmap_from_xft_font(Display *display, XftFont *font, FcChar32 ch,
1438			  unsigned char *pixels, unsigned w, unsigned h,
1439			  unsigned xmin, unsigned ymin)
1440{
1441    /*
1442     * FIXME: cache:
1443     * - the bitmap for the last M characters and target dimensions
1444     * - resuse the pixmap object where possible
1445     */
1446    XftColor bg, fg;
1447    Pixmap bitmap;
1448    XftDraw *draw;
1449    XImage *image;
1450    unsigned bmw, bmh;
1451    unsigned xx, yy;
1452
1453    bg.pixel = 0UL;
1454    bg.color.red = 0;
1455    bg.color.green = 0;
1456    bg.color.blue = 0;
1457    bg.color.alpha = 0x0;
1458
1459    fg.pixel = 1UL;
1460    fg.color.red = 0xffff;
1461    fg.color.green = 0xffff;
1462    fg.color.blue = 0xffff;
1463    fg.color.alpha = 0xffff;
1464
1465    bmw = w + xmin;
1466    bmh = h;
1467    bitmap = XCreatePixmap(display,
1468			   DefaultRootWindow(display),
1469			   bmw, bmh,
1470			   1);
1471    if (bitmap == None) {
1472	TRACE(("Unable to create Pixmap\n"));
1473	return 0;
1474    }
1475    draw = XftDrawCreateBitmap(display, bitmap);
1476    if (!draw) {
1477	TRACE(("Unable to create XftDraw\n"));
1478	XFreePixmap(display, bitmap);
1479	return 0;
1480    }
1481
1482    XftDrawRect(draw, &bg, 0, 0, bmw, bmh);
1483    XftDrawString32(draw, &fg, font, 0, font->ascent - (int) ymin,
1484		    &ch, 1);
1485
1486    image = XGetImage(display, bitmap, (int) xmin, 0, w, h, 1, XYPixmap);
1487    if (!image) {
1488	TRACE(("Unable to create XImage\n"));
1489	XftDrawDestroy(draw);
1490	XFreePixmap(display, bitmap);
1491	return 0;
1492    }
1493
1494    for (yy = 0U; yy < h; yy++) {
1495	for (xx = 0U; xx < w; xx++) {
1496	    pixels[yy * w + xx] = (unsigned char) XGetPixel(image,
1497							    (int) xx,
1498							    (int) yy);
1499	}
1500    }
1501
1502    XDestroyImage(image);
1503    XftDrawDestroy(draw);
1504    XFreePixmap(display, bitmap);
1505    return 1;
1506}
1507
1508static void
1509get_xft_glyph_dimensions(Display *display, XftFont *font, unsigned *w,
1510			 unsigned *h, unsigned *xmin, unsigned *ymin)
1511{
1512    unsigned workw, workh;
1513    FcChar32 ch;
1514    unsigned char *pixels;
1515    unsigned yy, xx;
1516    unsigned char_count, pixel_count;
1517    unsigned real_minx, real_maxx, real_miny, real_maxy;
1518    unsigned char_minx, char_maxx, char_miny, char_maxy;
1519
1520    /*
1521     * For each ASCII or ISO-8859-1 printable code, find out what its
1522     * dimensions are.
1523     *
1524     * We actually render the glyphs and determine the extents ourselves
1525     * because the font library can lie by several pixels, and since we are
1526     * doing manual character placement in fixed areas the glyph boundary needs
1527     * to be accurate.
1528     *
1529     * Ignore control characters and spaces - their extent information is
1530     * misleading.
1531     */
1532
1533    /* Our "work area" is just a buffer which should be big enough to hold the
1534     * largest glyph even if its size is under-reported by a couple of pixels
1535     * in each dimension.
1536     */
1537    workw = (unsigned) font->max_advance_width + 2U;
1538    if (font->ascent + font->descent > font->height) {
1539	workh = (unsigned) (font->ascent + font->descent) + 2U;
1540    } else {
1541	workh = (unsigned) font->height + 2U;
1542    }
1543
1544    if (!(pixels = malloc(workw * workh))) {
1545	*w = 0U;
1546	*h = 0U;
1547	return;
1548    }
1549
1550    /* FIXME: ch is in UCS32 -- try to support non-ASCII characters */
1551    char_count = 0U;
1552    real_minx = workw - 1U;
1553    real_maxx = 0U;
1554    real_miny = workh - 1U;
1555    real_maxy = 0U;
1556    for (ch = 33; ch < 256; ++ch) {
1557	if (ch >= 127 && ch <= 160)
1558	    continue;
1559	if (!FcCharSetHasChar(font->charset, ch))
1560	    continue;
1561
1562	copy_bitmap_from_xft_font(display, font, ch, pixels,
1563				  workw, workh, 0U, 0U);
1564
1565	pixel_count = 0U;
1566	char_minx = workh - 1U;
1567	char_maxx = 0U;
1568	char_miny = workh - 1U;
1569	char_maxy = 0U;
1570	for (yy = 0U; yy < workh; yy++) {
1571	    for (xx = 0U; xx < workw; xx++) {
1572		if (pixels[yy * workw + xx]) {
1573		    if (xx < char_minx)
1574			char_minx = xx;
1575		    if (xx > char_maxx)
1576			char_maxx = xx;
1577		    if (yy < char_miny)
1578			char_miny = yy;
1579		    if (yy > char_maxy)
1580			char_maxy = yy;
1581		    pixel_count++;
1582		}
1583	    }
1584	}
1585	if (pixel_count < 1U)
1586	    continue;
1587
1588#ifdef DEBUG_SPECIFIC_CHAR_METRICS
1589	if (IS_DEBUG_CHAR(ch)) {
1590	    printf("char: '%c' (%d)\n", (char) ch, ch);
1591	    printf(" minx: %u\n", char_minx);
1592	    printf(" maxx: %u\n", char_maxx);
1593	    printf(" miny: %u\n", char_miny);
1594	    printf(" maxy: %u\n", char_maxy);
1595	    dump_bitmap_pixels(pixels, workw, workh);
1596	    printf("\n");
1597	}
1598#endif
1599
1600	if (char_minx < real_minx)
1601	    real_minx = char_minx;
1602	if (char_maxx > real_maxx)
1603	    real_maxx = char_maxx;
1604	if (char_miny < real_miny)
1605	    real_miny = char_miny;
1606	if (char_maxy > real_maxy)
1607	    real_maxy = char_maxy;
1608	char_count++;
1609    }
1610
1611    free(pixels);
1612
1613    if (char_count < 1U) {
1614	*w = 0U;
1615	*h = 0U;
1616	return;
1617    }
1618
1619    *w = (unsigned) (1 + real_maxx - real_minx);
1620    *h = (unsigned) (1 + real_maxy - real_miny);
1621    *xmin = real_minx;
1622    *ymin = real_miny;
1623
1624#ifdef DEBUG_COMPUTED_FONT_METRICS
1625    printf("reported metrics:\n");
1626    printf(" %ux%u ascent=%u descent=%u\n", font->max_advance_width,
1627	   font->height, font->ascent, font->descent);
1628    printf("computed metrics:\n");
1629    printf(" real_minx=%u real_maxx=%u real_miny=%u real_maxy=%u\n",
1630	   real_minx, real_maxx, real_miny, real_maxy);
1631    printf(" final: %ux%u xmin=%u ymin=%u\n", *w, *h, *xmin, *ymin);
1632#endif
1633}
1634
1635#define FONT_SIZE_CACHE_SIZE 32U
1636
1637/* Find the font pixel size which returns the font which is closest to the given
1638 * maxw and maxh without overstepping either dimension.
1639 */
1640static XftFont *
1641find_best_xft_font_size(Display *display, Screen *screen, char const *fontname,
1642			unsigned maxw, unsigned maxh, unsigned max_pixels,
1643			unsigned *w, unsigned *h,
1644			unsigned *xmin, unsigned *ymin)
1645{
1646    XftFont *font;
1647    unsigned targeth;
1648    unsigned ii, cacheindex;
1649    static struct {
1650	char fontname[REGIS_FONTNAME_LEN];
1651	unsigned maxw, maxh, max_pixels;
1652	unsigned targeth;
1653	unsigned w, h;
1654	unsigned xmin;
1655	unsigned ymin;
1656    } cache[FONT_SIZE_CACHE_SIZE];
1657
1658    assert(display);
1659    assert(screen);
1660    assert(fontname);
1661    assert(w);
1662    assert(h);
1663    assert(xmin);
1664    assert(ymin);
1665
1666    cacheindex = FONT_SIZE_CACHE_SIZE;
1667    for (ii = 0U; ii < FONT_SIZE_CACHE_SIZE; ii++) {
1668	if (cache[ii].maxw == maxw && cache[ii].maxh == maxh &&
1669	    cache[ii].max_pixels == max_pixels &&
1670	    strcmp(cache[ii].fontname, fontname) == 0) {
1671	    cacheindex = ii;
1672	    break;
1673	}
1674    }
1675
1676    if (cacheindex < FONT_SIZE_CACHE_SIZE) {
1677	targeth = cache[cacheindex].targeth;
1678    } else {
1679	targeth = maxh * 10U + 5U;
1680    }
1681    for (;;) {
1682	if (targeth <= 5U) {
1683	    TRACE(("Giving up finding suitable Xft font size for %ux%u.\n",
1684		   maxw, maxh));
1685	    return NULL;
1686	}
1687
1688	/*
1689	 * Xft does a bad job at:
1690	 *  - two-color low-resolution anti-aliased fonts
1691	 *  - non-anti-aliased fonts at low resolution unless a font size is
1692	 *    given (pixel size does not help, and the value of the font size
1693	 *    doesn't appear to matter).
1694	 *
1695	 * In those two cases it literally drops pixels, sometimes whole
1696	 * columns, making the glyphs unreadable and ugly even when readable.
1697	 */
1698	/*
1699	 * FIXME:
1700	 * Also, we need to scale the width and height separately.  The
1701	 * CHAR_WIDTH and CHAR_HEIGHT attributes would seem to be ideal, but
1702	 * don't appear to have any effect if set.  Instead we will manually
1703	 * scale the bitmap later, which may be very ugly because we won't try
1704	 * to identify different parts of glyphs or preserve density.
1705	 */
1706	{
1707	    XftPattern *pat;
1708	    XftPattern *match;
1709	    XftResult status;
1710
1711	    font = NULL;
1712	    /* FIXME: make this name configurable.  In practice it may not be
1713	     * useful -- there are few fonts which meet all the requirements.
1714	     */
1715	    if ((pat = XftNameParse(fontname))) {
1716		XftPatternBuild(pat,
1717		/* arbitrary value */
1718				XFT_SIZE, XftTypeDouble, 12.0,
1719				XFT_PIXEL_SIZE, XftTypeDouble, (double)
1720				targeth / 10.0,
1721#if 0
1722				XFT_CHAR_WIDTH, XftTypeInteger, (int) maxw,
1723				XFT_CHAR_HEIGHT, XftTypeInteger, (int)
1724				(targeth / 10U),
1725#endif
1726				XFT_SPACING, XftTypeInteger, XFT_MONO,
1727				XFT_SLANT, XftTypeInteger, 0,
1728				XFT_ANTIALIAS, XftTypeBool, False,
1729				NULL);
1730		if ((match = XftFontMatch(display,
1731					  XScreenNumberOfScreen(screen),
1732					  pat, &status))) {
1733		    font = XftFontOpenPattern(display, match);
1734		}
1735	    }
1736	}
1737	if (!font) {
1738	    TRACE(("Unable to open a monospaced Xft font."));
1739	    return NULL;
1740	}
1741#ifdef DEBUG_FONT_SIZE_SEARCH
1742	{
1743	    char buffer[1024];
1744
1745	    if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer))) {
1746		printf("Testing font named \"%s\"\n", buffer);
1747	    } else {
1748		printf("Testing unknown font\n");
1749	    }
1750	}
1751#endif
1752
1753	if (cacheindex < FONT_SIZE_CACHE_SIZE &&
1754	    targeth == cache[cacheindex].targeth) {
1755	    *w = cache[cacheindex].w;
1756	    *h = cache[cacheindex].h;
1757	    *xmin = cache[cacheindex].xmin;
1758	    *ymin = cache[cacheindex].ymin;
1759	} else {
1760	    get_xft_glyph_dimensions(display, font, w, h, xmin, ymin);
1761	}
1762#ifdef DEBUG_FONT_SIZE_SEARCH
1763	printf("checking max=%ux%u targeth=%u.%u\n", maxw, maxh, targeth /
1764	       10U, targeth % 10U);
1765#endif
1766
1767	if (*h > maxh) {
1768	    XftFontClose(display, font);
1769#ifdef DEBUG_FONT_SIZE_SEARCH
1770	    printf("got %ux%u glyph; too tall; reducing target size\n", *w, *h);
1771#endif
1772	    if (*h > 2U * maxh) {
1773		targeth /= (*h / maxh);
1774	    } else if (targeth > 10U && *h > maxh + 1U) {
1775		targeth -= 10U;
1776	    } else {
1777		targeth--;
1778	    }
1779	    continue;
1780	}
1781	if (*w > maxw) {
1782	    XftFontClose(display, font);
1783#ifdef DEBUG_FONT_SIZE_SEARCH
1784	    printf("got %ux%u glyph; too wide; reducing target size\n", *w, *h);
1785#endif
1786	    if (*w > 2U * maxw) {
1787		targeth /= (*w / maxw);
1788	    } else if (targeth > 10U && *w > maxw + 1U) {
1789		targeth -= 10U;
1790	    } else {
1791		targeth--;
1792	    }
1793	    continue;
1794	}
1795	if (*w * *h > max_pixels) {
1796	    XftFontClose(display, font);
1797#ifdef DEBUG_FONT_SIZE_SEARCH
1798	    printf("got %ux%u glyph; too many pixels; reducing target size\n",
1799		   *w, *h);
1800#endif
1801	    if (*w * *h > 2U * max_pixels) {
1802		unsigned min = *w < *h ? *w : *h;
1803		unsigned divisor = (*w * *h) / (max_pixels * min);
1804		if (divisor > 1U) {
1805		    targeth /= divisor;
1806		} else if (targeth > 10U) {
1807		    targeth -= 10U;
1808		} else {
1809		    targeth--;
1810		}
1811	    } else {
1812		targeth--;
1813	    }
1814	    continue;
1815	}
1816#ifdef DEBUG_FONT_NAME
1817	{
1818	    char buffer[1024];
1819
1820	    if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer))) {
1821		printf("Final font for \"%s\" max %dx%d is \"%s\"\n",
1822		       fontname, maxw, maxh, buffer);
1823	    } else {
1824		printf("Final font for \"%s\" max %dx%d is unknown\n",
1825		       fontname, maxw, maxh);
1826	    }
1827	}
1828#endif
1829
1830	if (cacheindex == FONT_SIZE_CACHE_SIZE) {
1831	    for (ii = 0U; ii < FONT_SIZE_CACHE_SIZE; ii++) {
1832		if (cache[ii].maxw == 0U || cache[ii].maxh == 0U ||
1833		    cache[ii].max_pixels == 0U) {
1834		    CopyFontname(cache[ii].fontname, fontname);
1835		    cache[ii].maxw = maxw;
1836		    cache[ii].maxh = maxh;
1837		    cache[ii].max_pixels = max_pixels;
1838		    cache[ii].targeth = targeth;
1839		    cache[ii].w = *w;
1840		    cache[ii].h = *h;
1841		    cache[ii].xmin = *xmin;
1842		    cache[ii].ymin = *ymin;
1843		    break;
1844		}
1845	    }
1846	    if (ii == FONT_SIZE_CACHE_SIZE) {
1847		ii = targeth % FONT_SIZE_CACHE_SIZE;
1848		CopyFontname(cache[ii].fontname, fontname);
1849		cache[ii].maxw = maxw;
1850		cache[ii].maxh = maxh;
1851		cache[ii].max_pixels = max_pixels;
1852		cache[ii].targeth = targeth;
1853		cache[ii].w = *w;
1854		cache[ii].h = *h;
1855		cache[ii].xmin = *xmin;
1856		cache[ii].ymin = *ymin;
1857	    }
1858	}
1859	return font;
1860    }
1861}
1862#endif
1863
1864static int
1865get_xft_bitmap_of_character(RegisGraphicsContext const *context,
1866			    char const *fontname, char ch,
1867			    unsigned maxw, unsigned maxh, unsigned char *pixels,
1868			    unsigned max_pixels, unsigned *w, unsigned *h)
1869{
1870    /*
1871     * See Xft / RENDERFONT stuff in fontutils.c and used in utils.c
1872     * Add a separate configuration for ReGIS.
1873     */
1874    /*
1875     * FIXME: cache:
1876     * - resuse the font where possible
1877     */
1878#ifdef XRENDERFONT
1879    Display *display = XtDisplay(context->graphic->xw);
1880    Screen *screen = XtScreen(context->graphic->xw);
1881    XftFont *font;
1882    unsigned xmin = 0U, ymin = 0U;
1883
1884    if (!(font = find_best_xft_font_size(display, screen, fontname, maxw, maxh,
1885					 max_pixels, w, h, &xmin, &ymin))) {
1886	TRACE(("Unable to find suitable Xft font\n"));
1887	return 0;
1888    }
1889
1890    if (!copy_bitmap_from_xft_font(display, font, CharOf(ch), pixels, *w, *h,
1891				   xmin, ymin)) {
1892	TRACE(("Unable to create bitmap for '%c'\n", ch));
1893	XftFontClose(display, font);
1894	return 0;
1895    }
1896    XftFontClose(display, font);
1897    return 1;
1898#else
1899    (void) context;
1900    (void) context;
1901    (void) ch;
1902    (void) maxw;
1903    (void) maxh;
1904    (void) pixels;
1905    (void) max_pixels;
1906    (void) w;
1907    (void) h;
1908
1909    return 0;
1910#endif
1911}
1912
1913static unsigned
1914find_best_alphabet_index(RegisGraphicsContext const *context,
1915			 unsigned minw, unsigned minh,
1916			 unsigned maxw, unsigned maxh,
1917			 unsigned max_pixels)
1918{
1919    unsigned ii;
1920    unsigned bestmatch;
1921    unsigned bestw, besth;
1922
1923    assert(context);
1924    assert(maxw);
1925    assert(maxh);
1926
1927    bestmatch = MAX_REGIS_ALPHABETS;
1928    bestw = 0U;
1929    besth = 0U;
1930    for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
1931	if (context->alphabets[ii].alphabet_num ==
1932	    context->current_text_controls->alphabet_num &&
1933	    context->alphabets[ii].pixw >= minw &&
1934	    context->alphabets[ii].pixh >= minh &&
1935	    context->alphabets[ii].pixw <= maxw &&
1936	    context->alphabets[ii].pixh <= maxh &&
1937	    context->alphabets[ii].pixw > bestw &&
1938	    context->alphabets[ii].pixh > besth &&
1939	    context->alphabets[ii].pixw *
1940	    context->alphabets[ii].pixh <= max_pixels) {
1941	    bestmatch = ii;
1942	    bestw = context->alphabets[ii].pixw;
1943	    besth = context->alphabets[ii].pixh;
1944	}
1945    }
1946
1947    if (bestmatch < MAX_REGIS_ALPHABETS) {
1948	TRACE(("found alphabet %u at index %u size %ux%u font=%s\n",
1949	       context->current_text_controls->alphabet_num, bestmatch,
1950	       bestw, besth,
1951	       context->alphabets[bestmatch].use_font ?
1952	       context->alphabets[bestmatch].fontname : "(none)"));
1953    }
1954
1955    return bestmatch;
1956}
1957
1958#define GLYPH_WIDTH_BYTES(PIXW) ( ((PIXW) + 7U) >> 3U )
1959
1960static int
1961get_user_bitmap_of_character(RegisGraphicsContext const *context,
1962			     char ch,
1963			     unsigned alphabet_index,
1964			     unsigned char *pixels)
1965{
1966    const unsigned char *glyph;
1967    unsigned w, h;
1968    unsigned xx, yy;
1969    unsigned byte, bit;
1970
1971    assert(context);
1972    assert(pixels);
1973
1974    if (!context->alphabets[alphabet_index].loaded[(unsigned char) ch]) {
1975	TRACE(("in alphabet %u with alphabet index %u user glyph for '%c' not loaded\n",
1976	       context->current_text_controls->alphabet_num, alphabet_index,
1977	       ch));
1978	return 0;
1979    }
1980
1981    assert(context->alphabets[alphabet_index].bytes);
1982
1983    w = context->alphabets[alphabet_index].pixw;
1984    h = context->alphabets[alphabet_index].pixh;
1985    glyph = &context->alphabets[alphabet_index]
1986	.bytes[(unsigned char) ch * GLYPH_WIDTH_BYTES(w) * h];
1987
1988    for (yy = 0U; yy < h; yy++) {
1989	for (xx = 0U; xx < w; xx++) {
1990	    byte = yy * GLYPH_WIDTH_BYTES(w) + (xx >> 3U);
1991	    bit = xx & 7U;
1992	    pixels[yy * w + xx] = ((unsigned) glyph[byte] >> (7U - bit)) & 1U;
1993	}
1994    }
1995
1996    return 1;
1997}
1998
1999/*
2000 * alphabets
2001 *  0    built-in
2002 *  1-N  custom (max is 3 on VT3X0 -- up to MAX_REGIS_ALPHABETS with xterm)
2003 *
2004 * built-in 7-bit charsets
2005 *  (B    ASCII
2006 *  (0    DEC special graphics
2007 *  (>    DEC technical
2008 *  (A    NCR British
2009 *  (4    NCR Dutch
2010 *  (5    NCR Finnish
2011 *  (R    NCR French
2012 *  (9    NCR French Canadian
2013 *  (K    NCR German
2014 *  (Y    NCR Italian
2015 *  ('    NCR Norwegian/Danish
2016 *  (!6   NCR Portuguese
2017 *  (Z    NCR Spanish
2018 *  (7    NCR Swedish
2019 *  (-    NCR Swiss
2020 *
2021 * -@   ???
2022 *
2023 * built-in 8-bit charsets
2024 *  )%5   DEC supplemental graphics
2025 *  -A    ISO Latin-1 supplemental
2026 *  )<    user-preferred supplemental (94 chars)
2027 *
2028 * defaults
2029 *  terminal  char cell size   charsets      angle
2030 *  VT3x0     S1               0:ASCII(94)   0 (positive)
2031 *
2032 */
2033static void
2034get_bitmap_of_character(RegisGraphicsContext const *context, char ch,
2035			unsigned maxw, unsigned maxh, unsigned char *pixels,
2036			unsigned *w, unsigned *h, unsigned max_pixels)
2037{
2038    unsigned bestmatch;
2039    char const *fontname = NULL;
2040
2041    if (context->current_text_controls->alphabet_num == 0) {
2042	fontname = context->builtin_font;
2043    }
2044
2045    *w = 0U;
2046    *h = 0U;
2047
2048    bestmatch = find_best_alphabet_index(context, 1U, 1U, maxw, maxh,
2049					 max_pixels);
2050    if (bestmatch < MAX_REGIS_ALPHABETS) {
2051	RegisAlphabet const *alpha = &context->alphabets[bestmatch];
2052
2053	if (!alpha->use_font &&
2054	    get_user_bitmap_of_character(context, ch, bestmatch, pixels)) {
2055	    TRACE(("found user glyph for alphabet number %d (index %u)\n\n",
2056		   context->current_text_controls->alphabet_num, bestmatch));
2057	    *w = alpha->pixw;
2058	    *h = alpha->pixh;
2059	    return;
2060	}
2061
2062	if (alpha->use_font)
2063	    fontname = alpha->fontname;
2064    }
2065
2066    if (fontname) {
2067	if (get_xft_bitmap_of_character(context, fontname, ch,
2068					maxw, maxh, pixels,
2069					max_pixels, w, h)) {
2070	    if (*w > maxw) {
2071		TRACE(("BUG: Xft glyph is too wide: %ux%u but max is %ux%u\n",
2072		       *w, *h, maxw, maxh));
2073	    } else if (*h > maxh) {
2074		TRACE(("BUG: Xft glyph is too tall: %ux%u but max is %ux%u\n",
2075		       *w, *h, maxw, maxh));
2076	    } else if (*w * *h > max_pixels) {
2077		TRACE(("BUG: Xft glyph has too many pixels: %u but max is %u\n",
2078		       *w * *h, max_pixels));
2079	    } else {
2080		TRACE(("got glyph from \"%s\" for alphabet number %d\n",
2081		       fontname, context->current_text_controls->alphabet_num));
2082#ifdef DEBUG_SPECIFIC_CHAR_METRICS
2083		if (IS_DEBUG_CHAR(ch)) {
2084		    printf("got %ux%u Xft bitmap for '%c' target size %ux%u:\n",
2085			   *w, *h,
2086			   ch, maxw, maxh);
2087		    dump_bitmap_pixels(pixels, *w, *h);
2088		    printf("\n");
2089		}
2090#endif
2091		return;
2092	    }
2093	}
2094    }
2095
2096    TRACE(("unable to load any bitmap for character '%c' in alphabet number %u at %ux%u\n",
2097	   ch, context->current_text_controls->alphabet_num, maxw, maxh));
2098
2099    /* FIXME: this should probably produce an "unknown character" symbol */
2100    {
2101	unsigned xx, yy;
2102
2103	*w = MIN2(8U, maxh);
2104	*h = MIN2(10U, maxw);
2105	for (yy = 0U; yy < *h; yy++)
2106	    for (xx = 0U; xx < *w; xx++)
2107		pixels[yy * *w + xx] = '\0';
2108    }
2109}
2110
2111#define ROT_SHEAR_SCALE 8192
2112#define SIGNED_UNSIGNED_MOD(VAL, BASE) ( (((VAL) % (int) (BASE)) + (int) (BASE)) % (int) (BASE) )
2113
2114static unsigned
2115get_shade_character_pixel(unsigned char const *pixels, unsigned w, unsigned h,
2116			  unsigned smaxf, unsigned scale, int slant_dx,
2117			  int px, int py)
2118{
2119    unsigned wx, wy;
2120    unsigned fx, fy;
2121
2122    wx = (unsigned) SIGNED_UNSIGNED_MOD(px -
2123					(slant_dx * SIGNED_UNSIGNED_MOD(py, smaxf))
2124					/ ROT_SHEAR_SCALE, smaxf);
2125    wy = (unsigned) SIGNED_UNSIGNED_MOD(py, smaxf);
2126
2127    fx = (wx * scale) >> SCALE_FIXED_POINT;
2128    fy = (wy * scale) >> SCALE_FIXED_POINT;
2129    if (fx < w && fy < h) {
2130	return (unsigned) pixels[fy * w + fx];
2131    }
2132    return 0U;
2133}
2134
2135static void
2136draw_character(RegisGraphicsContext *context, char ch,
2137	       int slant_dx, int rot_shear_x,
2138	       int rot_shear_y, int x_sign_x, int x_sign_y,
2139	       int y_sign_x, int y_sign_y)
2140{
2141    const unsigned xmaxd = context->current_text_controls->character_display_w;
2142    const unsigned ymaxd = context->current_text_controls->character_display_h;
2143    const unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
2144    const unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
2145    unsigned w, h;
2146    unsigned xscale, yscale;
2147    unsigned fx, fy;
2148    unsigned px, py;
2149    int sx;
2150    int rx, ry;
2151    int ox, oy;
2152    unsigned pad_left, pad_right;
2153    unsigned pad_top, pad_bottom;
2154    unsigned char pixels[MAX_GLYPH_PIXELS];
2155    unsigned value;
2156
2157    get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h,
2158			    MAX_GLYPH_PIXELS);
2159    if (w < 1 || h < 1) {
2160	return;
2161    }
2162
2163    if (xmaxd > xmaxf) {
2164	pad_left = (xmaxd - xmaxf) / 2U;
2165	pad_right = (xmaxd - xmaxf) - pad_left;
2166    } else {
2167	pad_left = 0U;
2168	pad_right = 0U;
2169    }
2170    if (ymaxd > ymaxf) {
2171	pad_top = (ymaxd - ymaxf) / 2U;
2172	pad_bottom = (ymaxd - ymaxf) - pad_top;
2173    } else {
2174	pad_top = 0U;
2175	pad_bottom = 0U;
2176    }
2177
2178    xscale = (w << SCALE_FIXED_POINT) / xmaxf;
2179    yscale = (h << SCALE_FIXED_POINT) / ymaxf;
2180
2181    for (py = 0U; py < ymaxd; py++) {
2182	for (px = 0U; px < xmaxd; px++) {
2183	    if (py < pad_top || px < pad_left ||
2184		py >= ymaxd - pad_bottom || px >= xmaxd - pad_right) {
2185		value = 0U;
2186	    } else {
2187		fx = ((px - pad_left) * xscale) >> SCALE_FIXED_POINT;
2188		fy = ((py - pad_top) * yscale) >> SCALE_FIXED_POINT;
2189		if (fx < w && fy < h) {
2190		    value = (unsigned) pixels[fy * w + fx];
2191		} else {
2192		    value = 0U;
2193		}
2194	    }
2195
2196	    sx = (int) px + (slant_dx * (int) py) / ROT_SHEAR_SCALE;
2197	    rx = x_sign_x * sx + x_sign_y * (int) py;
2198	    ry = y_sign_x * sx + y_sign_y * (int) py;
2199	    ox = rx + (rot_shear_x * ry) / ROT_SHEAR_SCALE;
2200	    oy = ry + (rot_shear_y * ox) / ROT_SHEAR_SCALE;
2201	    ox += (rot_shear_x * oy) / ROT_SHEAR_SCALE;
2202
2203	    draw_regis_pixel(context,
2204			     (int) context->graphics_output_cursor_x + ox,
2205			     (int) context->graphics_output_cursor_y + oy,
2206			     value);
2207	}
2208    }
2209}
2210
2211static void
2212draw_text(RegisGraphicsContext *context, char const *str)
2213{
2214    double total_rotation;
2215    size_t ii;
2216    int str_invert;
2217    int str_shear_x, str_shear_y;
2218    int slant_dx;
2219    int chr_x_sign_x, chr_x_sign_y;
2220    int chr_y_sign_x, chr_y_sign_y;
2221    int chr_shear_x, chr_shear_y;
2222    int begin_x, begin_y;
2223    int rx, ry;
2224    int ox, oy;
2225
2226#ifdef DEBUG_ALPHABETS
2227    {
2228	unsigned n;
2229
2230	for (n = 0U; n < MAX_REGIS_ALPHABETS; n++) {
2231	    printf("alphabet index %u\n", n);
2232	    if (context->alphabets[n].alphabet_num != INVALID_ALPHABET_NUM) {
2233		printf(" alphabet_num=%u\n", context->alphabets[n].alphabet_num);
2234		printf(" pixw=%d\n", context->alphabets[n].pixw);
2235		printf(" pixh=%d\n", context->alphabets[n].pixh);
2236		printf(" name=\"%s\"\n", context->alphabets[n].name);
2237		printf(" use_font=%d\n", context->alphabets[n].use_font);
2238		printf(" fontname=\"%s\"\n", context->alphabets[n].fontname);
2239		printf(" bytes=%p\n", context->alphabets[n].bytes);
2240	    }
2241	}
2242    }
2243#endif
2244
2245    if (context->current_text_controls->slant <= -75 ||
2246	context->current_text_controls->slant >= +75) {
2247	TRACE(("ERROR: unsupported character slant angle %d\n",
2248	       context->current_text_controls->slant));
2249	return;
2250    }
2251
2252    /* FIXME: grab when first entering command */
2253    begin_x = context->graphics_output_cursor_x;
2254    begin_y = context->graphics_output_cursor_y;
2255
2256    total_rotation = 2.0 * M_PI *
2257	context->current_text_controls->string_rotation / 360.0;
2258    while (total_rotation > 1.5 * M_PI) {
2259	total_rotation -= 2.0 * M_PI;
2260    }
2261    if (total_rotation > 0.5 * M_PI) {
2262	total_rotation -= M_PI;
2263	str_invert = -1;
2264    } else {
2265	str_invert = 1;
2266    }
2267    str_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation));
2268    str_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation));
2269
2270    total_rotation = 2.0 * M_PI *
2271	context->current_text_controls->character_rotation / 360.0;
2272    while (total_rotation > 1.5 * M_PI) {
2273	total_rotation -= 2.0 * M_PI;
2274    }
2275    if (total_rotation > 0.5 * M_PI) {
2276	total_rotation -= M_PI;
2277	chr_x_sign_x = -1;
2278	chr_x_sign_y = 0;
2279	chr_y_sign_x = 0;
2280	chr_y_sign_y = -1;
2281    } else {
2282	chr_x_sign_x = 1;
2283	chr_x_sign_y = 0;
2284	chr_y_sign_x = 0;
2285	chr_y_sign_y = 1;
2286    }
2287    chr_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation));
2288    chr_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation));
2289    /*
2290     * FIXME: it isn't clear from the docs how slant affects the x positioning.
2291     * For now the code assumes the upper left is fixed.
2292     */
2293    TRACE(("float version: %.5f\n",
2294	   tan(2.0 * M_PI * abs(context->current_text_controls->slant) /
2295	       360.0)));
2296    if (context->current_text_controls->slant < 0) {
2297	slant_dx = (int) +(
2298			      tan(2.0 * M_PI * abs(context->current_text_controls->slant)
2299				  / 360.0) * ROT_SHEAR_SCALE);
2300    } else if (context->current_text_controls->slant > 0) {
2301	slant_dx = (int) -(
2302			      tan(2.0 * M_PI * abs(context->current_text_controls->slant)
2303				  / 360.0) * ROT_SHEAR_SCALE);
2304    } else {
2305	slant_dx = 0;
2306    }
2307    TRACE(("string rotation: %d\n",
2308	   context->current_text_controls->string_rotation));
2309    TRACE(("character rotation: %d\n",
2310	   context->current_text_controls->character_rotation));
2311    TRACE(("character slant: %d (%.5f pixels per line)\n",
2312	   context->current_text_controls->slant,
2313	   slant_dx / (double) ROT_SHEAR_SCALE));
2314    TRACE(("str_shear: %.5f, %.5f (sign=%d)\n",
2315	   str_shear_x / (double) ROT_SHEAR_SCALE,
2316	   str_shear_y / (double) ROT_SHEAR_SCALE,
2317	   str_invert));
2318    TRACE(("chr_shear: %.5f, %.5f (xsign=%d,%d, ysign=%d,%d)\n",
2319	   chr_shear_x / (double) ROT_SHEAR_SCALE,
2320	   chr_shear_y / (double) ROT_SHEAR_SCALE,
2321	   chr_x_sign_x, chr_x_sign_y,
2322	   chr_y_sign_x, chr_y_sign_y));
2323
2324    rx = 0;
2325    ry = 0;
2326    for (ii = 0U; ii < strlen(str); ii++) {
2327	switch (str[ii]) {
2328	case '\r':
2329	    rx = 0;
2330	    break;
2331	case '\n':
2332	    /* FIXME: verify */
2333	    ry += (int) context->current_text_controls->character_display_h;
2334	    break;
2335	case '\b':
2336	    rx -= context->current_text_controls->character_inc_x;
2337	    ry -= context->current_text_controls->character_inc_y;
2338	    break;
2339	case '\t':
2340	    rx += context->current_text_controls->character_inc_x;
2341	    ry += context->current_text_controls->character_inc_y;
2342	    break;
2343	default:
2344	    ox = str_invert * rx + (str_shear_x * ry) / ROT_SHEAR_SCALE;
2345	    oy = str_invert * ry + (str_shear_y * ox) / ROT_SHEAR_SCALE;
2346	    ox += (str_shear_x * oy) / ROT_SHEAR_SCALE;
2347	    context->graphics_output_cursor_x = begin_x + ox;
2348	    context->graphics_output_cursor_y = begin_y + oy;
2349	    draw_character(context, str[ii], slant_dx,
2350			   chr_shear_x, chr_shear_y,
2351			   chr_x_sign_x, chr_x_sign_y,
2352			   chr_y_sign_x, chr_y_sign_y);
2353	    rx += context->current_text_controls->character_inc_x;
2354	    ry += context->current_text_controls->character_inc_y;
2355	}
2356    }
2357
2358    ox = rx + (str_shear_x * ry) / ROT_SHEAR_SCALE;
2359    oy = ry + (str_shear_y * ox) / ROT_SHEAR_SCALE;
2360    ox += (str_shear_x * oy) / ROT_SHEAR_SCALE;
2361    context->graphics_output_cursor_x = begin_x + ox;
2362    context->graphics_output_cursor_y = begin_y + oy;
2363
2364    return;
2365}
2366
2367/*
2368 * standard character cell sizes
2369 *   number  disp cell   unit cell       offset
2370 *   S0      [  9, 10]   [  8, disp_h]   [disp_w, 0]
2371 *   S1      [  9, 20]   [  8, disp_h]   [disp_w, 0]
2372 *   S2      [ 18, 30]   [ 16, disp_h]   [disp_w, 0]
2373 *   S3      [ 27, 45]   [ 24, disp_h]   [disp_w, 0]
2374 *   S4      [ 36, 60]   [ 32, disp_h]   [disp_w, 0]
2375 *   S5      [ 45, 75]   [ 40, disp_h]   [disp_w, 0]
2376 *   S6      [ 54, 90]   [ 48, disp_h]   [disp_w, 0]
2377 *   S7      [ 63,105]   [ 56, disp_h]   [disp_w, 0]
2378 *   S8      [ 72,120]   [ 64, disp_h]   [disp_w, 0]
2379 *   S9      [ 81,135]   [ 72, disp_h]   [disp_w, 0]
2380 *   S10     [ 90,150]   [ 80, disp_h]   [disp_w, 0]
2381 *   S11     [ 99,165]   [ 88, disp_h]   [disp_w, 0]
2382 *   S12     [108,180]   [ 96, disp_h]   [disp_w, 0]
2383 *   S13     [117,195]   [104, disp_h]   [disp_w, 0]
2384 *   S14     [126,210]   [112, disp_h]   [disp_w, 0]
2385 *   S15     [135,225]   [120, disp_h]   [disp_w, 0]
2386 *   S16     [144,240]   [128, disp_h]   [disp_w, 0]
2387 */
2388static int
2389get_standard_character_size(int standard, unsigned *disp_w, unsigned
2390			    *disp_h, unsigned *unit_w, unsigned *unit_h,
2391			    int *off_x, int *off_y)
2392{
2393    switch (standard) {
2394    case 0:
2395	*disp_w = 9U;
2396	*disp_h = 10U;
2397	*unit_w = 8U;
2398	break;
2399    case 1:
2400	*disp_w = 9U;
2401	*disp_h = 20U;
2402	*unit_w = 8U;
2403	break;
2404    case 2:
2405	*disp_w = 18U;
2406	*disp_h = 30U;
2407	*unit_w = 16U;
2408	break;
2409    case 3:
2410	*disp_w = 27U;
2411	*disp_h = 45U;
2412	*unit_w = 24U;
2413	break;
2414    case 4:
2415	*disp_w = 36U;
2416	*disp_h = 60U;
2417	*unit_w = 32U;
2418	break;
2419    case 5:
2420	*disp_w = 45U;
2421	*disp_h = 75U;
2422	*unit_w = 40U;
2423	break;
2424    case 6:
2425	*disp_w = 54U;
2426	*disp_h = 90U;
2427	*unit_w = 48U;
2428	break;
2429    case 7:
2430	*disp_w = 63U;
2431	*disp_h = 105U;
2432	*unit_w = 56U;
2433	break;
2434    case 8:
2435	*disp_w = 72U;
2436	*disp_h = 120U;
2437	*unit_w = 64U;
2438	break;
2439    case 9:
2440	*disp_w = 81U;
2441	*disp_h = 135U;
2442	*unit_w = 72U;
2443	break;
2444    case 10:
2445	*disp_w = 90U;
2446	*disp_h = 150U;
2447	*unit_w = 80U;
2448	break;
2449    case 11:
2450	*disp_w = 99U;
2451	*disp_h = 165U;
2452	*unit_w = 88U;
2453	break;
2454    case 12:
2455	*disp_w = 108U;
2456	*disp_h = 180U;
2457	*unit_w = 96U;
2458	break;
2459    case 13:
2460	*disp_w = 117U;
2461	*disp_h = 195U;
2462	*unit_w = 104U;
2463	break;
2464    case 14:
2465	*disp_w = 126U;
2466	*disp_h = 210U;
2467	*unit_w = 112U;
2468	break;
2469    case 15:
2470	*disp_w = 135U;
2471	*disp_h = 225U;
2472	*unit_w = 120U;
2473	break;
2474    case 16:
2475	*disp_w = 144U;
2476	*disp_h = 240U;
2477	*unit_w = 128U;
2478	break;
2479    default:
2480	return 1;
2481    }
2482    *unit_h = *disp_h;
2483    *off_x = (int) *disp_w;
2484    *off_y = 0;
2485
2486    return 0;
2487}
2488
2489static void
2490init_fragment(RegisDataFragment *fragment, char const *str)
2491{
2492    assert(fragment);
2493    assert(str);
2494
2495    fragment->start = str;
2496    fragment->len = (unsigned) strlen(str);
2497    fragment->pos = 0U;
2498}
2499
2500static void
2501copy_fragment(RegisDataFragment *dst, RegisDataFragment const *src)
2502{
2503    assert(dst);
2504    assert(src);
2505
2506    dst->start = src->start;
2507    dst->len = src->len;
2508    dst->pos = src->pos;
2509}
2510
2511static char
2512peek_fragment(RegisDataFragment const *fragment)
2513{
2514    assert(fragment);
2515
2516    if (fragment->pos < fragment->len) {
2517	return fragment->start[fragment->pos];
2518    }
2519    return '\0';
2520}
2521
2522static char
2523pop_fragment(RegisDataFragment *fragment)
2524{
2525    assert(fragment);
2526
2527    if (fragment->pos < fragment->len) {
2528	return fragment->start[fragment->pos++];
2529    }
2530    return '\0';
2531}
2532
2533static size_t
2534fragment_len(RegisDataFragment const *fragment)
2535{
2536    assert(fragment);
2537
2538    return fragment->len - fragment->pos;
2539}
2540
2541static void
2542fragment_to_string(RegisDataFragment const *fragment, char *out,
2543		   unsigned outlen)
2544{
2545    unsigned remaininglen;
2546    unsigned endpos;
2547
2548    assert(fragment);
2549    assert(out);
2550
2551    if (!outlen)
2552	return;
2553    remaininglen = fragment->len - fragment->pos;
2554    if (remaininglen < outlen - 1U) {
2555	endpos = remaininglen;
2556    } else {
2557	endpos = outlen - 1U;
2558    }
2559    strncpy(out, &fragment->start[fragment->pos], endpos);
2560    out[endpos] = '\0';
2561}
2562
2563#define MAX_FRAG 1024
2564static char const *
2565fragment_to_tempstr(RegisDataFragment const *fragment)
2566{
2567    static char tempstr[MAX_FRAG];
2568
2569    assert(fragment);
2570
2571    fragment_to_string(fragment, tempstr, MAX_FRAG);
2572    return tempstr;
2573}
2574
2575static int
2576skip_regis_whitespace(RegisDataFragment *input)
2577{
2578    int skipped = 0;
2579    char ch;
2580
2581    assert(input);
2582
2583    for (; input->pos < input->len; input->pos++) {
2584	ch = input->start[input->pos];
2585	if (ch != ',' && !IsSpace(ch)) {
2586	    break;
2587	}
2588	if (ch == '\n') {
2589	    TRACE(("end of input line\n\n"));
2590	}
2591	skipped = 1;
2592    }
2593
2594    if (skipped)
2595	return 1;
2596    return 0;
2597}
2598
2599static int
2600extract_regis_extent(RegisDataFragment *input, RegisDataFragment *output)
2601{
2602    char ch;
2603
2604    assert(input);
2605    assert(output);
2606
2607    output->start = &input->start[input->pos];
2608    output->len = 0U;
2609    output->pos = 0U;
2610
2611    if (input->pos >= input->len)
2612	return 0;
2613
2614    ch = input->start[input->pos];
2615    if (ch != '[')
2616	return 0;
2617    input->pos++;
2618    output->start++;
2619
2620    /* FIXME: truncate to 16 bit signed integers */
2621    for (; input->pos < input->len; input->pos++, output->len++) {
2622	ch = input->start[input->pos];
2623	if (ch == ';') {
2624	    TRACE(("DATA_ERROR: end of input before closing bracket\n"));
2625	    break;
2626	}
2627	if (ch == ']')
2628	    break;
2629    }
2630    if (ch == ']')
2631	input->pos++;
2632
2633    return 1;
2634}
2635
2636static int
2637extract_regis_num(RegisDataFragment *input, RegisDataFragment *output)
2638{
2639    char ch = 0;
2640    int has_digits = 0;
2641
2642    assert(input);
2643    assert(output);
2644
2645    output->start = &input->start[input->pos];
2646    output->len = 0U;
2647    output->pos = 0U;
2648
2649    if (input->start[input->pos] == '-' ||
2650	input->start[input->pos] == '+') {
2651	input->pos++;
2652	output->len++;
2653    }
2654
2655    for (; input->pos < input->len; input->pos++, output->len++) {
2656	ch = input->start[input->pos];
2657	if (ch != '0' && ch != '1' && ch != '2' && ch != '3' &&
2658	    ch != '4' && ch != '5' && ch != '6' && ch != '7' &&
2659	    ch != '8' && ch != '9') {
2660	    break;
2661	}
2662	has_digits = 1;
2663    }
2664
2665    /* FIXME: what degenerate forms should be accepted ("E10" "1E" "1e" "1." "1ee10")? */
2666    /* FIXME: the terminal is said to support "floating point values", truncating to int... what do these look like? */
2667    if (has_digits && ch == 'E') {
2668	input->pos++;
2669	output->len++;
2670	for (; input->pos < input->len; input->pos++, output->len++) {
2671	    ch = input->start[input->pos];
2672	    if (ch != '0' && ch != '1' && ch != '2' && ch != '3' &&
2673		ch != '4' && ch != '5' && ch != '6' && ch != '7' &&
2674		ch != '8' && ch != '9') {
2675		break;
2676	    }
2677	}
2678    }
2679
2680    return has_digits;
2681}
2682
2683static int
2684extract_regis_pixelvector(RegisDataFragment *input, RegisDataFragment *output)
2685{
2686    char ch;
2687    int has_digits;
2688
2689    assert(input);
2690    assert(output);
2691
2692    output->start = &input->start[input->pos];
2693    output->len = 0U;
2694    output->pos = 0U;
2695
2696    if (input->pos < input->len) {
2697	ch = input->start[input->pos];
2698	if (ch == '+' || ch == '-') {
2699	    input->pos++;
2700	    output->len++;
2701	}
2702    }
2703
2704    has_digits = 0;
2705    for (; input->pos < input->len; input->pos++, output->len++) {
2706	ch = input->start[input->pos];
2707	if (ch != '0' && ch != '1' && ch != '2' && ch != '3' &&
2708	    ch != '4' && ch != '5' && ch != '6' && ch != '7') {
2709	    break;
2710	}
2711	has_digits = 1;
2712    }
2713
2714    return has_digits;
2715}
2716
2717static int
2718extract_regis_command(RegisDataFragment *input, char *command)
2719{
2720    char ch;
2721
2722    assert(input);
2723    assert(command);
2724
2725    if (input->pos >= input->len)
2726	return 0;
2727
2728    ch = input->start[input->pos];
2729    if (ch == '\0' || ch == ';') {
2730	return 0;
2731    }
2732    if (!islower(CharOf(ch)) && !isupper(CharOf(ch))) {
2733	return 0;
2734    }
2735    *command = ch;
2736    input->pos++;
2737
2738    return 1;
2739}
2740
2741static int
2742extract_regis_string(RegisDataFragment *input, char *out, unsigned maxlen)
2743{
2744    char first_ch;
2745    char ch;
2746    char prev_ch;
2747    unsigned outlen = 0U;
2748
2749    assert(input);
2750    assert(out);
2751
2752    if (input->pos >= input->len)
2753	return 0;
2754
2755    ch = input->start[input->pos];
2756    if (ch != '\'' && ch != '"')
2757	return 0;
2758    first_ch = ch;
2759    input->pos++;
2760
2761    ch = '\0';
2762    for (; input->pos < input->len; input->pos++) {
2763	prev_ch = ch;
2764	ch = input->start[input->pos];
2765	/* ';' (resync) is not recognized in strings */
2766	if (prev_ch == first_ch) {
2767	    if (ch == first_ch) {
2768		if (outlen < maxlen) {
2769		    out[outlen] = ch;
2770		}
2771		outlen++;
2772		ch = '\0';
2773		continue;
2774	    }
2775	    if (outlen < maxlen)
2776		out[outlen] = '\0';
2777	    else
2778		out[maxlen] = '\0';
2779	    return 1;
2780	}
2781	if (ch == '\0')
2782	    break;
2783	if (ch != first_ch) {
2784	    if (outlen < maxlen) {
2785		out[outlen] = ch;
2786	    }
2787	    outlen++;
2788	}
2789    }
2790    if (ch == first_ch) {
2791	if (outlen < maxlen)
2792	    out[outlen] = '\0';
2793	else
2794	    out[maxlen] = '\0';
2795	return 1;
2796    }
2797    /* FIXME: handle multiple strings concatenated with commas */
2798
2799    TRACE(("DATA_ERROR: end of input before closing quote\n"));
2800    return 0;
2801}
2802
2803static int
2804extract_regis_parenthesized_data(RegisDataFragment *input,
2805				 RegisDataFragment *output)
2806{
2807    char ch;
2808    char first_ch, prev_ch;
2809    int nesting;
2810
2811    assert(input);
2812    assert(output);
2813
2814    output->start = &input->start[input->pos];
2815    output->len = 0U;
2816    output->pos = 0U;
2817
2818    if (input->pos >= input->len)
2819	return 0;
2820
2821    ch = input->start[input->pos];
2822    if (ch != '(')
2823	return 0;
2824    input->pos++;
2825    output->start++;
2826    nesting = 1;
2827    first_ch = '\0';
2828
2829    ch = '\0';
2830    for (; input->pos < input->len; input->pos++, output->len++) {
2831	prev_ch = ch;
2832	ch = input->start[input->pos];
2833	if (ch == '\'' || ch == '"') {
2834	    if (first_ch == '\0') {
2835		first_ch = ch;
2836	    } else {
2837		if (ch == prev_ch && prev_ch == first_ch) {
2838		    ch = '\0';
2839		} else if (ch == first_ch) {
2840		    first_ch = '\0';
2841		}
2842	    }
2843	    continue;
2844	}
2845	if (first_ch != '\0')
2846	    continue;
2847
2848	if (ch == ';') {
2849	    TRACE(("leaving parenthesized data nested %d levels deep due to command termination character\n",
2850		   nesting));
2851	    break;
2852	}
2853	if (ch == '(')
2854	    nesting++;
2855	if (ch == ')') {
2856	    nesting--;
2857	    if (nesting == 0) {
2858		input->pos++;
2859		return 1;
2860	    }
2861	}
2862    }
2863
2864    TRACE(("DATA_ERROR: end of input before closing paren (%d levels deep)\n",
2865	   nesting));
2866    return 0;
2867}
2868
2869static int
2870extract_regis_option(RegisDataFragment *input,
2871		     char *option,
2872		     RegisDataFragment *output)
2873{
2874    char ch;
2875    int paren_level, bracket_level;
2876    char first_ch;
2877
2878    assert(input);
2879    assert(option);
2880    assert(output);
2881
2882    /* LETTER suboptions* value? */
2883    /*
2884     * FIXME: what are the rules for using separate parens vs. sharing between
2885     * options?
2886     */
2887
2888    output->start = &input->start[input->pos];
2889    output->len = 0U;
2890    output->pos = 0U;
2891
2892    if (input->pos >= input->len) {
2893	return 0;
2894    }
2895
2896    ch = input->start[input->pos];
2897    /* FIXME: are options always letters or are some special characters ok? */
2898    if (ch == ';' || ch == ',' ||
2899	ch == '(' || ch == ')' ||
2900	ch == '[' || ch == ']' ||
2901	ch == '"' || ch == '\'' ||
2902	isdigit(CharOf(ch))) {
2903	return 0;
2904    }
2905    *option = ch;
2906    input->pos++;
2907    output->start++;
2908    paren_level = 0;
2909    bracket_level = 0;
2910
2911    first_ch = '\0';
2912    for (; input->pos < input->len; input->pos++, output->len++) {
2913	ch = input->start[input->pos];
2914	TRACE(("looking at char '%c' in option '%c'\n", ch, *option));
2915	/* FIXME: any special rules for commas? */
2916	/* FIXME: handle escaped quotes */
2917	if (ch == '\'' || ch == '"') {
2918	    if (first_ch == ch) {
2919		first_ch = '\0';
2920	    } else {
2921		first_ch = ch;
2922	    }
2923	    continue;
2924	}
2925	if (first_ch != '\0')
2926	    continue;
2927	if (ch == '(') {
2928	    paren_level++;
2929	}
2930	if (ch == ')') {
2931	    paren_level--;
2932	    if (paren_level < 0) {
2933		TRACE(("DATA_ERROR: found ReGIS option has value with too many close parens \"%c\"\n",
2934		       *option));
2935		return 0;
2936	    }
2937	}
2938	if (ch == '[') {
2939	    bracket_level++;
2940	}
2941	if (ch == ']') {
2942	    bracket_level--;
2943	    if (bracket_level < 0) {
2944		TRACE(("DATA_ERROR: found ReGIS option has value with too many close brackets \"%c\"\n",
2945		       *option));
2946		return 0;
2947	    }
2948	}
2949	/*
2950	 * Top-level commas indicate the end of this option and the start of
2951	 * another.
2952	 */
2953	if (paren_level == 0 && bracket_level == 0 && ch == ',')
2954	    break;
2955	/*
2956	 * Top-level command/option/suboption names also indicate the end of
2957	 * this option.  "E" is valid as the exponent indicator in a numeric
2958	 * parameter.
2959	 */
2960	if (paren_level == 0 && bracket_level == 0 && ch != 'E' && ch != 'e' &&
2961	    ((ch > 'A' && ch < 'Z') || (ch > 'a' && ch < 'z')))
2962	    break;
2963	if (ch == ';')
2964	    break;
2965    }
2966    if (paren_level != 0) {
2967	TRACE(("DATA_ERROR: mismatched parens in argument to ReGIS option \"%c\"\n",
2968	       *option));
2969	return 0;
2970    }
2971    if (bracket_level != 0) {
2972	TRACE(("DATA_ERROR: mismatched brackets in argument to ReGIS option \"%c\"\n",
2973	       *option));
2974	return 0;
2975    }
2976
2977    TRACE(("found ReGIS option and value \"%c\" \"%s\"\n",
2978	   *option,
2979	   fragment_to_tempstr(output)));
2980    return 1;
2981}
2982
2983static int
2984regis_num_to_int(RegisDataFragment const *input, int *out)
2985{
2986    char ch;
2987
2988    /* FIXME: handle exponential notation and rounding */
2989    /* FIXME: check for junk after the number */
2990    ch = peek_fragment(input);
2991    if (ch != '0' &&
2992	ch != '1' &&
2993	ch != '2' &&
2994	ch != '3' &&
2995	ch != '4' &&
2996	ch != '5' &&
2997	ch != '6' &&
2998	ch != '7' &&
2999	ch != '8' &&
3000	ch != '9' &&
3001	ch != '+' &&
3002	ch != '-') {
3003	return 0;
3004    }
3005
3006    TRACE(("converting \"%s\" to an int\n", fragment_to_tempstr(input)));
3007    *out = atoi(fragment_to_tempstr(input));
3008    return 1;
3009}
3010
3011static int
3012load_regis_colorspec(RegisGraphicsContext const *context,
3013		     RegisDataFragment const *input,
3014		     short *r_out, short *g_out, short *b_out)
3015{
3016    RegisDataFragment colorspec;
3017    short r = -1, g = -1, b = -1;
3018    short h = -1, l = -1, s = -1;
3019
3020    copy_fragment(&colorspec, input);
3021    TRACE(("colorspec option: \"%s\"\n", fragment_to_tempstr(&colorspec)));
3022
3023    skip_regis_whitespace(&colorspec);
3024    if (fragment_len(&colorspec) == 1) {
3025	char ch = pop_fragment(&colorspec);
3026
3027	TRACE(("got ReGIS RGB colorspec pattern '%c' with arguments: \"%s\"\n",
3028	       ch, fragment_to_tempstr(&colorspec)));
3029	switch (ch) {
3030	case 'D':
3031	case 'd':
3032	    r = 0;
3033	    g = 0;
3034	    b = 0;
3035	    l = 0;
3036	    break;
3037	case 'R':
3038	case 'r':
3039	    r = 100;
3040	    g = 0;
3041	    b = 0;
3042	    l = 46;
3043	    break;
3044	case 'G':
3045	case 'g':
3046	    r = 0;
3047	    g = 100;
3048	    b = 0;
3049	    l = 50;
3050	    break;
3051	case 'B':
3052	case 'b':
3053	    r = 0;
3054	    g = 0;
3055	    b = 100;
3056	    l = 50;
3057	    break;
3058	case 'C':
3059	case 'c':
3060	    r = 0;
3061	    g = 100;
3062	    b = 100;
3063	    l = 50;
3064	    break;
3065	case 'Y':
3066	case 'y':
3067	    r = 100;
3068	    g = 100;
3069	    b = 0;
3070	    l = 50;
3071	    break;
3072	case 'M':
3073	case 'm':
3074	    r = 100;
3075	    g = 0;
3076	    b = 100;
3077	    l = 50;
3078	    break;
3079	case 'W':
3080	case 'w':
3081	    r = 100;
3082	    g = 100;
3083	    b = 100;
3084	    l = 100;
3085	    break;
3086	default:
3087	    TRACE(("unknown RGB color name: \"%c\"\n", ch));
3088	    return 0;
3089	}
3090    } else {
3091	RegisDataFragment num;
3092	int max, val;
3093	char comp;
3094
3095	while (colorspec.pos < colorspec.len) {
3096	    if (skip_regis_whitespace(&colorspec))
3097		continue;
3098
3099	    comp = pop_fragment(&colorspec);
3100	    switch (comp) {
3101	    case ',':
3102		/* not sure if this is valid, but it is easy to handle */
3103		continue;
3104	    case 'H':
3105	    case 'h':
3106		max = 360;
3107		comp = 'H';
3108		break;
3109	    case 'L':
3110	    case 'l':
3111		max = 100;
3112		comp = 'L';
3113		break;
3114	    case 'S':
3115	    case 's':
3116		max = 100;
3117		comp = 'S';
3118		break;
3119#ifdef ENABLE_RGB_COLORSPECS
3120	    case 'R':		/* RLogin extension */
3121	    case 'r':
3122		max = 100;
3123		comp = 'R';
3124		break;
3125	    case 'G':		/* RLogin extension */
3126	    case 'g':
3127		max = 100;
3128		comp = 'G';
3129		break;
3130	    case 'B':		/* RLogin extension */
3131	    case 'b':
3132		max = 100;
3133		comp = 'B';
3134		break;
3135#endif
3136	    default:
3137		TRACE(("unrecognized character in colorspec: \"%c\"\n", comp));
3138		return 0;
3139	    }
3140
3141	    skip_regis_whitespace(&colorspec);
3142	    if (!extract_regis_num(&colorspec, &num)) {
3143		TRACE(("unrecognized character in colorspec: \"%c\"\n", comp));
3144		return 0;
3145	    }
3146	    if (!regis_num_to_int(&num, &val)) {
3147		TRACE(("DATA_ERROR: component value %s is not a number\n",
3148		       fragment_to_tempstr(&num)));
3149		return 0;
3150	    }
3151	    /* FIXME: error, truncate, wrap, ...? */
3152	    if (val < 0 || val > max) {
3153		TRACE(("DATA_ERROR: component value %d out of range\n", val));
3154		return 0;
3155	    }
3156
3157	    switch (comp) {
3158	    case 'H':
3159		h = (short) val;
3160		break;
3161	    case 'L':
3162		l = (short) val;
3163		break;
3164	    case 'S':
3165		s = (short) val;
3166		break;
3167	    case 'R':
3168		r = (short) val;
3169		break;
3170	    case 'G':
3171		g = (short) val;
3172		break;
3173	    case 'B':
3174		b = (short) val;
3175		break;
3176	    }
3177	}
3178
3179	if (h >= 0 && l >= 0 && s >= 0 && r < 0 && g < 0 && b < 0) {
3180	    TRACE(("found HLS colorspec to be converted: %hd,%hd,%hd\n",
3181		   h, l, s));
3182	    hls2rgb(h, l, s, &r, &g, &b);
3183	    TRACE(("converted to RGB: %hd,%hd,%hd\n", r, g, b));
3184	} else if (h < 0 && l < 0 && s < 0 && r >= 0 && g >= 0 && b >= 0) {
3185	    TRACE(("found RGB colorspec: %hd,%hd,%hd\n", r, g, b));
3186	    l = (short) ((MIN3(r, g, b) + MAX3(r, g, b)) / 2);
3187	    TRACE(("calculated L: %d\n", l));
3188	} else if (h < 0 && l >= 0 && s < 0 && r < 0 && g < 0 && b < 0) {
3189	    TRACE(("found L colorspec to be converted: %hd,%hd,%hd\n",
3190		   h, l, s));
3191	    hls2rgb(0, l, 0, &r, &g, &b);
3192	    TRACE(("converted to RGB: %hd,%hd,%hd\n", r, g, b));
3193	} else {
3194	    TRACE(("unrecognized colorspec format\n"));
3195	    return 0;
3196	}
3197    }
3198
3199    /*
3200     * The VT240 and VT330 models convert to the closest grayscale value.
3201     */
3202    if (context->terminal_id == 240 || context->terminal_id == 330) {
3203	hls2rgb(0, l, 0, &r, &g, &b);
3204	TRACE(("converted to grayscale: %hd,%hd,%hd\n", r, g, b));
3205    }
3206
3207    *r_out = r;
3208    *g_out = g;
3209    *b_out = b;
3210
3211    if (colorspec.pos < colorspec.len) {
3212	char skip;
3213
3214	skip_regis_whitespace(&colorspec);
3215	skip = pop_fragment(&colorspec);
3216	(void) skip;		/* variable needed only if tracing */
3217	TRACE(("DATA_ERROR: ignoring unexpected character in ReGIS colorspec \"%c\"\n",
3218	       skip));
3219    }
3220
3221    return 1;
3222}
3223
3224static int
3225load_regis_regnum_or_colorspec(RegisGraphicsContext const *context,
3226			       RegisDataFragment const *input,
3227			       RegisterNum *out)
3228{
3229    int val;
3230    RegisDataFragment colorspec;
3231    RegisDataFragment num;
3232    RegisDataFragment coloroption;
3233
3234    copy_fragment(&colorspec, input);
3235    TRACE(("looking at colorspec pattern: \"%s\"\n",
3236	   fragment_to_tempstr(&colorspec)));
3237
3238    skip_regis_whitespace(&colorspec);
3239
3240    if (extract_regis_num(&colorspec, &num)) {
3241	if (!regis_num_to_int(&num, &val)) {
3242	    TRACE(("DATA_ERROR: colorspec value %s is not a valid register\n",
3243		   fragment_to_tempstr(&num)));
3244	    return 0;
3245	}
3246	if (val < 0) {
3247	    /* FIXME: error, truncate, wrap, ...? */
3248	    TRACE(("DATA_ERROR: ignoring negative colorspec value: %d\n", val));
3249	    return 0;
3250	}
3251	if (val >= (int) context->graphic->valid_registers) {
3252	    /* FIXME: error, truncate, wrap, ...? */
3253	    TRACE(("DATA_ERROR: colorspec value %d is too big; wrapping\n",
3254		   val));
3255	    val %= (int) context->graphic->valid_registers;
3256	}
3257
3258	TRACE(("colorspec contains index for register %u\n", val));
3259	*out = (RegisterNum) val;
3260
3261	if (colorspec.pos < colorspec.len) {
3262	    char skip;
3263
3264	    skip_regis_whitespace(&colorspec);
3265	    skip = pop_fragment(&colorspec);
3266	    (void) skip;	/* variable needed only if tracing */
3267	    TRACE(("DATA_ERROR: unexpected character after register \"%c\"\n",
3268		   skip));
3269	    return 0;
3270	}
3271
3272	return 1;
3273    }
3274
3275    if (extract_regis_parenthesized_data(&colorspec, &coloroption)) {
3276	short r, g, b;
3277
3278	if (!load_regis_colorspec(context, &coloroption, &r, &g, &b)) {
3279	    TRACE(("unable to parse colorspec\n"));
3280	    return 0;
3281	}
3282
3283	*out = find_color_register(context->graphic->color_registers, r, g, b);
3284	TRACE(("colorspec maps to closest register %u\n", *out));
3285
3286	return 1;
3287    }
3288
3289    TRACE(("expected register number or colorspec, but found: \"%s\"\n",
3290	   fragment_to_tempstr(&colorspec)));
3291    return 0;
3292}
3293
3294static int
3295to_scaled_int(char const *num, int scale, int *value)
3296{
3297    unsigned long whole, frac;
3298    char *end;
3299
3300    /* FIXME: handle whitespace? how about trailing junk? */
3301    whole = strtoul(num, &end, 10);
3302    if (end[0] == '.') {
3303	char temp[5] = "0000";
3304
3305	if (end[1] != '\0') {
3306	    temp[0] = end[1];
3307	    if (end[2] != '\0') {
3308		temp[1] = end[2];
3309		if (end[3] != '\0') {
3310		    temp[2] = end[3];
3311		    if (end[4] != '\0') {
3312			temp[3] = end[4];
3313		    }
3314		}
3315	    }
3316	}
3317	frac = strtoul(temp, NULL, 10);
3318    } else if (end[0] == '\0' || end[0] == ',') {
3319	frac = 0;
3320    } else {
3321	TRACE(("unexpected character %c in number %s\n", end[0], num));
3322	return 0;
3323    }
3324
3325    *value = (int) (whole * (unsigned) scale +
3326		    (frac * (unsigned) scale) / 10000);
3327
3328    return 1;
3329}
3330
3331static int
3332load_regis_raw_extent(char const *extent, int *relx, int *rely,
3333		      int *xloc, int *yloc, int scale)
3334{
3335    int xsign, ysign;
3336    char const *xpart;
3337    char const *ypart;
3338
3339    xpart = extent;
3340    if ((ypart = strchr(extent, ','))) {
3341	ypart++;
3342    } else {
3343	ypart = "";
3344    }
3345
3346    if (xpart[0] == '-') {
3347	xsign = -1;
3348	xpart++;
3349    } else if (xpart[0] == '+') {
3350	xsign = +1;
3351	xpart++;
3352    } else {
3353	xsign = 0;
3354    }
3355    if (ypart[0] == '-') {
3356	ysign = -1;
3357	ypart++;
3358    } else if (ypart[0] == '+') {
3359	ysign = +1;
3360	ypart++;
3361    } else {
3362	ysign = 0;
3363    }
3364
3365    if (xpart[0] == '\0' || xpart[0] == ',') {
3366	*relx = 1;
3367	*xloc = 0;
3368    } else if (xsign == 0) {
3369	int val;
3370
3371	if (!to_scaled_int(xpart, scale, &val))
3372	    return 0;
3373	*relx = 0;
3374	*xloc = val;
3375    } else {
3376	int val;
3377
3378	if (!to_scaled_int(xpart, scale, &val))
3379	    return 0;
3380	*relx = 1;
3381	*xloc = xsign * val;
3382    }
3383    if (ypart[0] == '\0') {
3384	*rely = 1;
3385	*yloc = 0;
3386    } else if (ysign == 0) {
3387	int val;
3388
3389	if (!to_scaled_int(ypart, scale, &val))
3390	    return 0;
3391	*rely = 0;
3392	*yloc = val;
3393    } else {
3394	int val;
3395
3396	if (!to_scaled_int(ypart, scale, &val))
3397	    return 0;
3398	*rely = 1;
3399	*yloc = ysign * val;
3400    }
3401
3402    return 1;
3403}
3404
3405static int
3406load_regis_pixel_extent(char const *extent, int origx, int origy,
3407			int *xloc, int *yloc)
3408{
3409    int relx, rely;
3410    int px, py;
3411
3412    if (!load_regis_raw_extent(extent, &relx, &rely, &px, &py, 1)) {
3413	TRACE(("invalid coordinates in extent %s\n", extent));
3414	return 0;
3415    }
3416
3417    *xloc = px;
3418    *yloc = py;
3419
3420    if (relx)
3421	*xloc += origx;
3422    if (rely)
3423	*yloc += origy;
3424
3425    return 1;
3426}
3427
3428#define COORD_SCALE 1000
3429
3430static int
3431load_regis_coord_extent(RegisGraphicsContext const *context, char const *extent,
3432			int origx, int origy, int *xloc, int *yloc)
3433{
3434    int relx, rely;
3435    int ux, uy;
3436
3437    if (!load_regis_raw_extent(extent, &relx, &rely, &ux, &uy, COORD_SCALE)) {
3438	TRACE(("invalid coordinates in extent %s\n", extent));
3439	return 0;
3440    }
3441
3442    if (relx) {
3443	const int px = SCALE_XCOORD(context, ux, COORD_SCALE);
3444	TRACE(("converted relative X coord %.03f to relative pixel coord %d (width=%d xoff=%d xdiv=%d)\n",
3445	       ux / (double) COORD_SCALE, px, context->width,
3446	       context->x_off, context->x_div));
3447	*xloc = origx + px;
3448    } else {
3449	const int px = TRANSLATE_XCOORD(context, ux, COORD_SCALE);
3450	TRACE(("converted absolute X coord %.03f to absolute pixel coord %d\n",
3451	       ux / (double) COORD_SCALE, px));
3452	*xloc = px;
3453    }
3454    if (rely) {
3455	const int py = SCALE_YCOORD(context, uy, COORD_SCALE);
3456	TRACE(("converted relative Y coord %.03f to relative pixel coord %d (height=%d, yoff=%d, ydiv=%d)\n",
3457	       uy / (double) COORD_SCALE, py, context->height,
3458	       context->y_off, context->y_div));
3459	*yloc = origy + py;
3460    } else {
3461	const int py = TRANSLATE_YCOORD(context, uy, COORD_SCALE);
3462	TRACE(("converted absolute Y coord %.03f to absolute pixel coord %d\n",
3463	       uy / (double) COORD_SCALE, py));
3464	*yloc = py;
3465    }
3466
3467    return 1;
3468}
3469
3470static int
3471load_regis_pixelvector(char const *pixelvector,
3472		       unsigned mul,
3473		       int origx, int origy,
3474		       int *xloc, int *yloc)
3475{
3476    int dx = 0, dy = 0;
3477    int i;
3478
3479    for (i = 0; pixelvector[i] != '\0'; i++) {
3480	switch (pixelvector[i]) {
3481	case '0':
3482	    dx += 1;
3483	    break;
3484	case '1':
3485	    dx += 1;
3486	    dy -= 1;
3487	    break;
3488	case '2':
3489	    dy -= 1;
3490	    break;
3491	case '3':
3492	    dx -= 1;
3493	    dy -= 1;
3494	    break;
3495	case '4':
3496	    dx -= 1;
3497	    break;
3498	case '5':
3499	    dx -= 1;
3500	    dy += 1;
3501	    break;
3502	case '6':
3503	    dy += 1;
3504	    break;
3505	case '7':
3506	    dx += 1;
3507	    dy += 1;
3508	    break;
3509	default:
3510	    break;
3511	}
3512    }
3513
3514    *xloc = origx + dx * (int) mul;
3515    *yloc = origy + dy * (int) mul;
3516
3517    return 1;
3518}
3519
3520static int
3521load_regis_write_control(RegisParseState *state,
3522			 RegisGraphicsContext const *context,
3523			 int cur_x, int cur_y,
3524			 int option,
3525			 RegisDataFragment *arg,
3526			 RegisWriteControls *out)
3527{
3528    TRACE(("checking write control option \"%c\" with arg \"%s\"\n",
3529	   option, fragment_to_tempstr(arg)));
3530    switch (option) {
3531    case 'C':
3532    case 'c':
3533	TRACE(("write control compliment writing mode \"%s\"\n",
3534	       fragment_to_tempstr(arg)));
3535	out->write_style = WRITE_STYLE_COMPLEMENT;
3536	break;
3537    case 'E':
3538    case 'e':
3539	TRACE(("write control erase writing mode \"%s\"\n",
3540	       fragment_to_tempstr(arg)));
3541	out->write_style = WRITE_STYLE_ERASE;
3542	break;
3543    case 'F':
3544    case 'f':
3545	TRACE(("write control plane write mask \"%s\"\n",
3546	       fragment_to_tempstr(arg)));
3547	{
3548	    int val;
3549	    if (!regis_num_to_int(arg, &val) ||
3550		val < 0 || val >= (int) context->graphic->valid_registers) {
3551		TRACE(("interpreting out of range value as 0 FIXME\n"));
3552		out->plane_mask = 0U;
3553	    } else {
3554		out->plane_mask = (unsigned) val;
3555	    }
3556	}
3557	break;
3558    case 'I':
3559    case 'i':
3560	TRACE(("write control foreground color \"%s\"\n",
3561	       fragment_to_tempstr(arg)));
3562	if (!load_regis_regnum_or_colorspec(context, arg, &out->foreground)) {
3563	    TRACE(("DATA_ERROR: write control foreground color specifier not recognized: \"%s\"\n",
3564		   fragment_to_tempstr(arg)));
3565	    return 0;
3566	}
3567	break;
3568    case 'L':
3569    case 'l':
3570	TRACE(("write control line width \"%s\" (FIXME: currently ignored)\n",
3571	       fragment_to_tempstr(arg)));
3572	{
3573	    int val;
3574	    if (!regis_num_to_int(arg, &val) ||
3575		val < 0 || val >= (int) 9) {
3576		TRACE(("interpreting out of range value as 1 FIXME\n"));
3577		out->line_width = 1U;
3578	    } else {
3579		out->line_width = (unsigned) val;
3580	    }
3581	}
3582	break;
3583    case 'M':
3584    case 'm':
3585	TRACE(("write control found pixel multiplication factor \"%s\"\n",
3586	       fragment_to_tempstr(arg)));
3587	{
3588	    int val;
3589	    if (!regis_num_to_int(arg, &val) || val <= 0) {
3590		TRACE(("interpreting out of range value %d as 1 FIXME\n", val));
3591		out->pv_multiplier = 1U;
3592	    } else {
3593		out->pv_multiplier = (unsigned) val;
3594	    }
3595	}
3596	break;
3597    case 'N':
3598    case 'n':
3599	TRACE(("write control negative pattern control \"%s\"\n",
3600	       fragment_to_tempstr(arg)));
3601	{
3602	    int val;
3603	    if (!regis_num_to_int(arg, &val)) {
3604		val = -1;
3605	    }
3606	    switch (val) {
3607	    default:
3608		TRACE(("interpreting out of range value %d as 0 FIXME\n", val));
3609		out->invert_pattern = 0U;
3610		break;
3611	    case 0:
3612		out->invert_pattern = 0U;
3613		break;
3614	    case 1:
3615		out->invert_pattern = 1U;
3616		break;
3617	    }
3618	}
3619	break;
3620    case 'P':
3621    case 'p':
3622	TRACE(("write control found pattern control \"%s\"\n",
3623	       fragment_to_tempstr(arg)));
3624	{
3625	    RegisDataFragment suboptionset;
3626	    RegisDataFragment suboptionarg;
3627	    RegisDataFragment item;
3628	    char suboption;
3629
3630	    while (arg->pos < arg->len) {
3631		if (skip_regis_whitespace(arg))
3632		    continue;
3633
3634		TRACE(("looking for option in \"%s\"\n",
3635		       fragment_to_tempstr(arg)));
3636		if (extract_regis_parenthesized_data(arg, &suboptionset)) {
3637		    TRACE(("got write pattern suboptionset: \"%s\"\n",
3638			   fragment_to_tempstr(&suboptionset)));
3639		    while (suboptionset.pos < suboptionset.len) {
3640			skip_regis_whitespace(&suboptionset);
3641			if (extract_regis_option(&suboptionset, &suboption,
3642						 &suboptionarg)) {
3643			    skip_regis_whitespace(&suboptionarg);
3644			    TRACE(("inspecting write pattern suboption \"%c\" with value \"%s\"\n",
3645				   suboption,
3646				   fragment_to_tempstr(&suboptionarg)));
3647			    switch (suboption) {
3648			    case 'M':
3649			    case 'm':
3650				TRACE(("found pattern multiplier \"%s\"\n",
3651				       fragment_to_tempstr(&suboptionarg)));
3652				{
3653				    RegisDataFragment num;
3654				    int val;
3655
3656				    if (extract_regis_num(&suboptionarg,
3657							  &num)) {
3658					if (!regis_num_to_int(&num, &val)
3659					    || val < 1) {
3660					    TRACE(("interpreting out of range pattern multiplier \"%s\" as 2 FIXME\n",
3661						   fragment_to_tempstr(&num)));
3662					    out->pattern_multiplier = 2U;
3663					} else {
3664					    out->pattern_multiplier =
3665						(unsigned) val;
3666					}
3667				    }
3668				    skip_regis_whitespace(&suboptionarg);
3669
3670				    if (fragment_len(&suboptionarg)) {
3671					TRACE(("DATA_ERROR: unknown content after pattern multiplier \"%s\"\n",
3672					       fragment_to_tempstr(&suboptionarg)));
3673					return 0;
3674				    }
3675				}
3676				break;
3677			    default:
3678				TRACE(("DATA_ERROR: unknown ReGIS write pattern suboption '%c' arg \"%s\"\n",
3679				       suboption,
3680				       fragment_to_tempstr(&suboptionarg)));
3681				return 0;
3682			    }
3683			    continue;
3684			}
3685
3686			TRACE(("DATA_ERROR: skipping unknown token in pattern control suboptionset (expecting option): \"%s\"\n",
3687			       fragment_to_tempstr(&suboptionset)));
3688			pop_fragment(&suboptionset);
3689		    }
3690		    continue;
3691		}
3692
3693		TRACE(("looking for int in \"%s\"\n",
3694		       fragment_to_tempstr(arg)));
3695		if (extract_regis_num(arg, &item)) {
3696		    if (peek_fragment(&item) == '0' ||
3697			peek_fragment(&item) == '1') {
3698			unsigned pattern = 0U;
3699			unsigned bitcount;
3700			char ch;
3701
3702			TRACE(("converting pattern bits \"%s\"\n",
3703			       fragment_to_tempstr(&item)));
3704			for (bitcount = 0;; bitcount++) {
3705			    ch = pop_fragment(&item);
3706			    if (ch == '\0')
3707				break;
3708			    switch (ch) {
3709			    case '0':
3710				if (bitcount < MAX_PATTERN_BITS) {
3711				    pattern <<= 1U;
3712				}
3713				break;
3714			    case '1':
3715				if (bitcount < MAX_PATTERN_BITS) {
3716				    pattern <<= 1U;
3717				    pattern |= 1U;
3718				}
3719				break;
3720			    default:
3721				TRACE(("DATA_ERROR: unknown ReGIS write pattern bit value \"%c\"\n",
3722				       ch));
3723				return 0;
3724			    }
3725			}
3726
3727			if (bitcount > 0U) {
3728			    unsigned extrabits;
3729
3730			    for (extrabits = 0;
3731				 bitcount + extrabits < MAX_PATTERN_BITS;
3732				 extrabits++) {
3733				if (pattern & (1U << (bitcount - 1U))) {
3734				    pattern <<= 1U;
3735				    pattern |= 1U;
3736				} else {
3737				    pattern <<= 1U;
3738				}
3739			    }
3740			}
3741
3742			out->pattern = pattern;
3743		    } else {
3744			int val;
3745
3746			TRACE(("converting pattern id \"%s\"\n",
3747			       fragment_to_tempstr(&item)));
3748			if (!regis_num_to_int(&item, &val))
3749			    val = -1;
3750			switch (val) {	/* FIXME: exponential allowed? */
3751			case 0:
3752			    out->pattern = 0x00;	/* solid bg */
3753			    break;
3754			case 1:
3755			    out->pattern = 0xff;	/* solid fg */
3756			    break;
3757			case 2:
3758			    out->pattern = 0xf0;	/* dash */
3759			    break;
3760			case 3:
3761			    out->pattern = 0xe4;	/* dash dot */
3762			    break;
3763			case 4:
3764			    out->pattern = 0xaa;	/* dot */
3765			    break;
3766			case 5:
3767			    out->pattern = 0xea;	/* dash dot dot */
3768			    break;
3769			case 6:
3770			    out->pattern = 0x88;	/* sparse dot */
3771			    break;
3772			case 7:
3773			    out->pattern = 0x84;	/* asymmetric sparse dot */
3774			    break;
3775			case 8:
3776			    out->pattern = 0xc8;	/* sparse dash dot */
3777			    break;
3778			case 9:
3779			    out->pattern = 0x86;	/* sparse dot dash */
3780			    break;
3781			default:
3782			    TRACE(("DATA_ERROR: unknown ReGIS standard write pattern \"%d\"\n",
3783				   val));
3784			    return 0;
3785			}
3786		    }
3787
3788		    TRACE(("final pattern is %02x\n", out->pattern));
3789		    continue;
3790		}
3791		skip_regis_whitespace(arg);
3792
3793		TRACE(("DATA_ERROR: skipping unknown token in pattern suboption: \"%s\"\n",
3794		       fragment_to_tempstr(arg)));
3795		pop_fragment(arg);
3796	    }
3797	}
3798	break;
3799    case 'R':
3800    case 'r':
3801	TRACE(("write control switch to replacement writing mode \"%s\"\n",
3802	       fragment_to_tempstr(arg)));
3803	out->write_style = WRITE_STYLE_REPLACE;
3804	break;
3805    case 'S':
3806    case 's':
3807	TRACE(("write control shading control \"%s\"\n",
3808	       fragment_to_tempstr(arg)));
3809	{
3810	    RegisDataFragment suboptionset;
3811	    RegisDataFragment suboptionarg;
3812	    RegisDataFragment item;
3813	    char suboption;
3814	    char shading_character = '\0';
3815	    unsigned reference_dim = WRITE_SHADING_REF_Y;
3816	    int ref_x = cur_x, ref_y = cur_y;
3817	    int shading_enabled = 0;
3818
3819	    while (arg->pos < arg->len) {
3820		if (skip_regis_whitespace(arg))
3821		    continue;
3822
3823		if (extract_regis_string(arg, state->temp, state->templen)) {
3824		    TRACE(("found fill char \"%s\"\n", state->temp));
3825		    /* FIXME: allow longer strings, ignore extra chars, or treat as error? */
3826		    if (strlen(state->temp) != 1) {
3827			TRACE(("DATA_ERROR: expected exactly one char in fill string FIXME\n"));
3828			return 0;
3829		    }
3830		    /* FIXME: should this turn shading on also? */
3831		    shading_character = state->temp[0];
3832		    shading_enabled = 1;
3833		    TRACE(("shading character is: '%c' (%d)\n",
3834			   shading_character, (int) shading_character));
3835		    continue;
3836		}
3837
3838		if (extract_regis_parenthesized_data(arg, &suboptionset)) {
3839		    skip_regis_whitespace(&suboptionset);
3840		    TRACE(("got shading control suboptionset: \"%s\"\n",
3841			   fragment_to_tempstr(&suboptionset)));
3842		    while (suboptionset.pos < suboptionset.len) {
3843			if (skip_regis_whitespace(&suboptionset)) {
3844			    continue;
3845			}
3846			if (extract_regis_option(&suboptionset, &suboption,
3847						 &suboptionarg)) {
3848			    TRACE(("inspecting write shading suboption \"%c\" with value \"%s\"\n",
3849				   suboption,
3850				   fragment_to_tempstr(&suboptionarg)));
3851			    switch (suboption) {
3852			    case 'X':
3853			    case 'x':
3854				TRACE(("found vertical shading suboption \"%s\"\n",
3855				       fragment_to_tempstr(&suboptionarg)));
3856				if (fragment_len(&suboptionarg)) {
3857				    TRACE(("DATA_ERROR: unexpected value to vertical shading suboption FIXME\n"));
3858				    return 0;
3859				}
3860				reference_dim = WRITE_SHADING_REF_X;
3861				break;
3862			    default:
3863				TRACE(("DATA_ERROR: unknown ReGIS write pattern suboption '%c' arg \"%s\"\n",
3864				       suboption,
3865				       fragment_to_tempstr(&suboptionarg)));
3866				return 0;
3867			    }
3868			    continue;
3869			}
3870
3871			TRACE(("DATA_ERROR: skipping unknown token in shading control suboptionset (expecting option): \"%s\"\n",
3872			       fragment_to_tempstr(&suboptionset)));
3873			pop_fragment(&suboptionset);
3874		    }
3875		    continue;
3876		}
3877
3878		if (extract_regis_extent(arg, &item)) {
3879		    if (!load_regis_coord_extent(context,
3880						 fragment_to_tempstr(&item),
3881						 ref_x, ref_y,
3882						 &ref_x, &ref_y)) {
3883			TRACE(("DATA_ERROR: unable to parse extent in write shading option '%c': \"%s\"\n",
3884			       option, fragment_to_tempstr(&item)));
3885			return 0;
3886		    }
3887		    TRACE(("shading reference = %d,%d (%s)\n", ref_x, ref_y,
3888			   ((reference_dim == WRITE_SHADING_REF_X)
3889			    ? "X"
3890			    : "Y")));
3891		    continue;
3892		}
3893
3894		if (extract_regis_num(arg, &item)) {
3895		    if (!regis_num_to_int(&item, &shading_enabled)) {
3896			TRACE(("DATA_ERROR: unable to parse int in write shading option '%c': \"%s\"\n",
3897			       option, fragment_to_tempstr(&item)));
3898			return 0;
3899		    }
3900		    if (shading_enabled < 0 || shading_enabled > 1) {
3901			TRACE(("interpreting out of range value %d as 0 FIXME\n",
3902			       shading_enabled));
3903			shading_enabled = 0;
3904		    }
3905		    TRACE(("shading enabled = %d\n", shading_enabled));
3906		    continue;
3907		}
3908
3909		if (skip_regis_whitespace(arg)) {
3910		    continue;
3911		}
3912
3913		TRACE(("DATA_ERROR: skipping unknown token in shade suboption: \"%s\"\n",
3914		       fragment_to_tempstr(arg)));
3915		pop_fragment(arg);
3916	    }
3917
3918	    if (shading_enabled) {
3919		out->shading_enabled = 1U;
3920		out->shading_reference_dim = reference_dim;
3921		out->shading_reference = ((reference_dim == WRITE_SHADING_REF_X)
3922					  ? ref_x
3923					  : ref_y);
3924		out->shading_character = shading_character;
3925	    } else {
3926		/* FIXME: confirm there is no effect if shading isn't enabled
3927		 * in the same command
3928		 */
3929		out->shading_enabled = 0U;
3930	    }
3931	}
3932	break;
3933    case 'V':
3934    case 'v':
3935	TRACE(("write control switch to overlay writing mode \"%s\"\n",
3936	       fragment_to_tempstr(arg)));
3937	out->write_style = WRITE_STYLE_OVERLAY;
3938	break;
3939    default:
3940	TRACE(("DATA_ERROR: ignoring unknown ReGIS write option \"%c\" arg \"%s\"\n",
3941	       option, fragment_to_tempstr(arg)));
3942	return 0;
3943    }
3944
3945    return 1;
3946}
3947
3948static int
3949load_regis_write_control_set(RegisParseState *state,
3950			     RegisGraphicsContext const *context,
3951			     int cur_x, int cur_y,
3952			     RegisDataFragment *controls,
3953			     RegisWriteControls *out)
3954{
3955    RegisDataFragment optionset;
3956    RegisDataFragment arg;
3957    char option;
3958
3959    while (controls->pos < controls->len) {
3960	if (skip_regis_whitespace(controls))
3961	    continue;
3962
3963	if (extract_regis_parenthesized_data(controls, &optionset)) {
3964	    TRACE(("got write control optionset: \"%s\"\n",
3965		   fragment_to_tempstr(&optionset)));
3966	    while (optionset.pos < optionset.len) {
3967		skip_regis_whitespace(&optionset);
3968		if (extract_regis_option(&optionset, &option, &arg)) {
3969		    skip_regis_whitespace(&arg);
3970		    TRACE(("got write control option and value: \"%c\" \"%s\"\n",
3971			   option, fragment_to_tempstr(&arg)));
3972		    if (!load_regis_write_control(state, context,
3973						  cur_x, cur_y,
3974						  option, &arg, out)) {
3975			return 0;
3976		    }
3977		    continue;
3978		}
3979
3980		TRACE(("DATA_ERROR: skipping unknown token in write control optionset (expecting option): \"%s\"\n",
3981		       fragment_to_tempstr(&optionset)));
3982		pop_fragment(&optionset);
3983	    }
3984	    continue;
3985	}
3986
3987	TRACE(("DATA_ERROR: skipping unknown token in write controls (expecting optionset): \"%s\"\n",
3988	       fragment_to_tempstr(controls)));
3989	pop_fragment(controls);
3990    }
3991
3992    return 1;
3993}
3994
3995static void
3996init_regis_write_controls(int terminal_id, unsigned all_planes,
3997			  RegisWriteControls *controls)
3998{
3999    controls->pv_multiplier = 1U;
4000    controls->pattern = 0xff;	/* solid */
4001    controls->pattern_multiplier = 2U;
4002    controls->invert_pattern = 0U;
4003    controls->plane_mask = all_planes;
4004    controls->write_style = WRITE_STYLE_OVERLAY;
4005    switch (terminal_id) {
4006    case 125:			/* FIXME */
4007    case 240:			/* FIXME */
4008    case 241:			/* FIXME */
4009    case 330:
4010	controls->foreground = 3U;
4011	break;
4012    case 340:
4013    default:
4014	controls->foreground = 7U;
4015	break;
4016    }
4017    controls->shading_enabled = 0U;
4018    controls->shading_character = '\0';
4019    controls->shading_reference = 0;	/* no meaning if shading is disabled */
4020    controls->shading_reference_dim = WRITE_SHADING_REF_Y;
4021    controls->line_width = 1U;
4022    /* FIXME: add the rest */
4023}
4024
4025static void
4026copy_regis_write_controls(RegisWriteControls const *src,
4027			  RegisWriteControls *dst)
4028{
4029    dst->pv_multiplier = src->pv_multiplier;
4030    dst->pattern = src->pattern;
4031    dst->pattern_multiplier = src->pattern_multiplier;
4032    dst->invert_pattern = src->invert_pattern;
4033    dst->foreground = src->foreground;
4034    dst->plane_mask = src->plane_mask;
4035    dst->write_style = src->write_style;
4036    dst->shading_enabled = src->shading_enabled;
4037    dst->shading_character = src->shading_character;
4038    dst->shading_reference = src->shading_reference;
4039    dst->shading_reference_dim = src->shading_reference_dim;
4040    dst->line_width = src->line_width;
4041}
4042
4043static void
4044init_regis_text_controls(RegisTextControls *controls)
4045{
4046    controls->alphabet_num = 0U;	/* built-in */
4047    controls->character_set_l = 0U;	/* ASCII */
4048    controls->character_set_r = 0U;	/* Latin-1 */
4049    get_standard_character_size(1, &controls->character_display_w,
4050				&controls->character_display_h,
4051				&controls->character_unit_cell_w,
4052				&controls->character_unit_cell_h,
4053				&controls->character_inc_x,
4054				&controls->character_inc_y);
4055    controls->string_rotation = 0;
4056    controls->character_rotation = 0;
4057    controls->slant = 0;
4058}
4059
4060static void
4061copy_regis_text_controls(RegisTextControls const *src, RegisTextControls *dst)
4062{
4063    dst->alphabet_num = src->alphabet_num;
4064    dst->character_set_l = src->character_set_l;
4065    dst->character_set_r = src->character_set_r;
4066    dst->character_display_w = src->character_display_w;
4067    dst->character_display_h = src->character_display_h;
4068    dst->character_unit_cell_w = src->character_unit_cell_w;
4069    dst->character_unit_cell_h = src->character_unit_cell_h;
4070    dst->character_inc_x = src->character_inc_x;
4071    dst->character_inc_y = src->character_inc_y;
4072    dst->string_rotation = src->string_rotation;
4073    dst->character_rotation = src->character_rotation;
4074    dst->slant = src->slant;
4075}
4076
4077static void
4078init_regis_alphabets(RegisGraphicsContext *context)
4079{
4080    unsigned alphabet_index;
4081
4082    for (alphabet_index = 0U; alphabet_index < MAX_REGIS_ALPHABETS;
4083	 alphabet_index++) {
4084	context->alphabets[alphabet_index].alphabet_num = INVALID_ALPHABET_NUM;
4085	context->alphabets[alphabet_index].pixw = 0U;
4086	context->alphabets[alphabet_index].pixh = 0U;
4087	context->alphabets[alphabet_index].name[0] = '\0';
4088	context->alphabets[alphabet_index].fontname[0] = '\0';
4089	context->alphabets[alphabet_index].use_font = 0;
4090	context->alphabets[alphabet_index].bytes = NULL;
4091    }
4092}
4093
4094static void
4095init_regis_graphics_context(int terminal_id, int width, int height,
4096			    unsigned max_colors, const char *builtin_font,
4097			    RegisGraphicsContext *context)
4098{
4099    context->graphic = NULL;
4100    context->terminal_id = terminal_id;
4101    context->width = width;
4102    context->height = height;
4103    context->x_off = 0;
4104    context->y_off = 0;
4105    context->x_div = width - 1;
4106    context->y_div = height - 1;
4107    /*
4108     * Generate a mask covering all valid color register address bits
4109     * (but don't bother past 2**16).
4110     */
4111    context->all_planes = max_colors;
4112    context->all_planes--;
4113    context->all_planes |= 1U;
4114    context->all_planes |= context->all_planes >> 1U;
4115    context->all_planes |= context->all_planes >> 2U;
4116    context->all_planes |= context->all_planes >> 4U;
4117    context->all_planes |= context->all_planes >> 8U;
4118
4119    context->builtin_font = builtin_font;
4120
4121    init_regis_write_controls(terminal_id, context->all_planes,
4122			      &context->persistent_write_controls);
4123    copy_regis_write_controls(&context->persistent_write_controls,
4124			      &context->temporary_write_controls);
4125
4126    init_regis_text_controls(&context->persistent_text_controls);
4127    context->current_text_controls = &context->persistent_text_controls;
4128    init_regis_alphabets(context);
4129
4130    context->multi_input_mode = 0;
4131    /* FIXME: coordinates */
4132    /* FIXME: scrolling */
4133    context->background = 0U;
4134    /* FIXME: input cursor location */
4135    /* FIXME: input cursor style */
4136    context->graphics_output_cursor_x = 0;
4137    context->graphics_output_cursor_y = 0;
4138    /* FIXME: output cursor style */
4139}
4140
4141static int
4142parse_regis_command(RegisParseState *state)
4143{
4144    char ch = peek_fragment(&state->input);
4145    if (ch == '\0')
4146	return 0;
4147
4148    if (!extract_regis_command(&state->input, &ch))
4149	return 0;
4150
4151    switch (ch) {
4152    case 'C':
4153    case 'c':
4154	/* Curve
4155
4156	 * C
4157	 * (A)  # set the arc length in degrees (+ or nothing for
4158	 *      # counter-clockwise, - for clockwise, rounded to the
4159	 *      # closest integer degree)
4160	 * (B)  # begin closed curve sequence (must have at least two
4161	 *      # values; this option can not be nested)
4162	 * (C)  # position is the center, current location is the
4163	 *      # circumference (stays in effect until next command)
4164	 * (E)  # end curve sequence (drawing is performed here)
4165	 * (S)  # begin open curve sequence
4166	 * (W)  # temporary write options (see write command)
4167	 * [<center, circumference position>]  # center if (C), otherwise point on circumference
4168	 * [<point in curve sequence>]...  # if between (B) and (E)
4169	 * <pv>...  # if between (B) and (E)
4170	 */
4171	TRACE(("found ReGIS command \"%c\" (curve)\n", ch));
4172	state->command = 'c';
4173	state->curve_mode = CURVE_POSITION_ARC_EDGE;
4174	state->arclen = 360;
4175	state->num_points = 0U;
4176	break;
4177    case 'F':
4178    case 'f':
4179	/* Fill
4180
4181	 * F
4182	 * (V)  # polygon (see vector command)
4183	 * (C)  # curve (see curve command)
4184	 * (W)  # temporary write options (see write command)
4185	 */
4186	TRACE(("found ReGIS command \"%c\" (filled polygon)\n", ch));
4187	state->command = 'f';
4188	break;
4189    case 'L':
4190    case 'l':
4191	/* Load
4192
4193	 * L
4194	 * (A)  # set character set number or name
4195	 * (F)"fontname"  # load from font (xterm extension)
4196	 * (S)[w,h]  # set glyph size (xterm extension)
4197	 * "ascii"xx,xx,xx,xx,xx,xx,xx,xx  # pixel values
4198	 */
4199	TRACE(("found ReGIS command \"%c\" (load charset)\n", ch));
4200	state->command = 'l';
4201	state->load_index = MAX_REGIS_ALPHABETS;
4202	state->load_w = 8U;
4203	state->load_h = 10U;
4204	state->load_alphabet = 1U;	/* FIXME: is this the correct default */
4205	state->load_name[0] = '\0';
4206	state->load_glyph = (unsigned) (unsigned char) '\0';
4207	state->load_row = 0U;
4208	break;
4209    case 'P':
4210    case 'p':
4211	/* Position
4212
4213	 * P
4214	 * (B)  # begin bounded position stack (last point returns to first)
4215	 * (E)  # end position stack
4216	 * (P)  # select graphics page for the input and output cursors
4217	 * (S)  # begin unbounded position stack
4218	 * (W)  # temporary write options (see write command)
4219	 * <pv>  # move: 0 == right, 1 == upper right, ..., 7 == lower right
4220	 * [<position>]  # move to position (X, Y, or both)
4221	 *
4222	 * Note the stack does not need to be ended before the next command
4223	 * Note: maximum depth is 16 levels
4224	 */
4225	TRACE(("found ReGIS command \"%c\" (position)\n", ch));
4226	state->command = 'p';
4227	break;
4228    case 'R':
4229    case 'r':
4230	/* Report
4231
4232	 * R
4233	 * (E)  # parse error
4234	 * (I<val>)  # set input mode (0 == oneshot, 1 == multiple) (always returns CR)
4235	 * (L)  # character set
4236	 * (M(<name>)  # macrograph contents
4237	 * (M(=)  # macrograph storage
4238	 * (P)  # output cursor position
4239	 * (P(I))  # input cursor position (when in oneshot or multiple mode)
4240	 */
4241	TRACE(("found ReGIS command \"%c\" (report status)\n", ch));
4242	state->command = 'r';
4243	break;
4244    case 'S':
4245    case 's':
4246	/* Screen
4247
4248	 * S
4249	 * (A[<upper left>][<lower right>])
4250	 * (C<setting>  # 0 (cursor output off), 1 (cursor output on)
4251	 * (E)  # erase to background color, resets shades, curves, and stacks
4252	 * (F)  # print the graphic and erase the screen (DECprint extension)
4253	 * (H(P<printer offset>)[<print area cornet>][<print area corner>)
4254	 * (I<color register>)  # set the background to a specific register
4255	 * (I(<rgbcode>))  # set the background to the register closest to an "RGB" color
4256	 * (I(R<r>G<g>B<b>))  # set the background to the register closest to an RGB triplet (RLogin extension)
4257	 * (I(H<h>L<l>S<s>))  # set the background to the register closest to an HLS triplet
4258	 * (I(L<l>))  # set the background to the register closest to a grayscale value
4259	 * (M<color index to set>(<rgbcode>)...)  # codes are D (black), R (red), G (green), B (blue), C (cyan), Y (yellow), M (magenta), W (white) (sets color and grayscale registers)
4260	 * (M<color index to set>(A<rgbcode>)...)  # codes are D (black), R (red), G (green), B (blue), C (cyan), Y (yellow), M (magenta), W (white) (sets color registers only)
4261	 * (M<color index to set>(R<red>G<green>B<blue>)...)  # 0..100, 0..100, 0..100 (sets color and grayscale registers) (RLogin extension)
4262	 * (M<color index to set>(AR<red>G<green>B<blue>)...)  # 0..100, 0..100, 0..100 (sets color registers only) (RLogin extension)
4263	 * (M<color index to set>(H<hue>L<lightness>S<saturation>)...)  # 0..360, 0..100, 0..100 (sets color and grayscale registers)
4264	 * (M<color index to set>(AH<hue>L<lightness>S<saturation>)...)  # 0..360, 0..100, 0..100 (sets color registers only)
4265	 * (M<color index to set>(L<mono level>)...)  # level is 0 ... 100 (sets grayscale registers only)
4266	 * (P<graphics page number>)  # 0 (default) or 1
4267	 * (T(<time delay ticks>)  # 60 ticks per second, up to 32767 ticks
4268	 * (W(M<factor>)  # PV multiplier
4269	 * <PV scroll offset>  # scroll data so given coordinate is at the upper-left
4270	 * [scroll offset]  # scroll data so given coordinate is at the upper-left
4271	 */
4272	TRACE(("found ReGIS command \"%c\" (screen)\n", ch));
4273	state->command = 's';
4274	break;
4275    case 'T':
4276    case 't':
4277	/* Text
4278
4279	 * T
4280	 * (A)  # specify which alphabet to select character sets from (0==builtin)
4281	 * (A0L"<designator>"))  # specify a built-in set for GL via two-char designator
4282	 * (A0R"<designator>"))  # specify a built-in set for GR via two-char or three-char designator
4283	 * (A<num>R"<designator>"))  # specify a user-loaded (1-3) set for GR via two-char or three-char designator
4284	 * (B)  # begin temporary text control
4285	 * (D<angle>)  # specify a string or character tilt
4286	 * (E)  # end temporary text control
4287	 * (H<factor>)  # select a height multiplier (1-256)
4288	 * (I<angle>)  # italic/oblique: no slant (0), lean back (-1 though -45), lean forward (+1 through +45)
4289	 * (M[width factor,height factor])  # select size multipliers (width 1-16) (height 1-256)
4290	 * (S<size id>)  # select one of the 17 standard character sizes
4291	 * (S[dimensions])  # set a custom display cell size (char with border)
4292	 * (U[dimensions])  # set a custom unit cell size (char size)
4293	 * (W<write command>)  # temporary write options (see write command)
4294	 * [<char offset>]  # optional offset between characters
4295	 * <PV spacing>  # for subscripts and superscripts
4296	 * '<text>'  # optional
4297	 * "<text>"  # optional
4298	 */
4299	TRACE(("found ReGIS command \"%c\" (text)\n", ch));
4300	state->command = 't';
4301	state->string_rot_set = 0;
4302	break;
4303    case 'V':
4304    case 'v':
4305	/* Vector
4306
4307	 * V
4308	 * (B)  # begin bounded position stack (last point returns to first)
4309	 * (E)  # end position stack
4310	 * (S)  # begin unbounded position stack
4311	 * (W)  # temporary write options (see write command)
4312	 * <pv>  # draw a line to the pixel vector
4313	 * []  # draw a dot at the current location
4314	 * [<position>]  # draw a line to position
4315	 */
4316	TRACE(("found ReGIS command \"%c\" (vector)\n", ch));
4317	state->command = 'v';
4318	break;
4319    case 'W':
4320    case 'w':
4321	/* Write
4322
4323	 * W
4324	 * (C)  # complement writing mode
4325	 * (E)  # erase writing mode
4326	 * (F<plane>)  # set the foreground intensity to a specific register
4327	 * (I<color register>)  # set the foreground to a specific register
4328	 * (I(<rgbcode>))  # set the foreground to the register closest to an "RGB" color
4329	 * (I(R<r>G<g>B<b>))  # set the foreground to the register closest to an RGB triplet (RLogin extension)
4330	 * (I(H<h>L<l>S<s>))  # set the foreground to the register closest to an HLS triplet
4331	 * (I(L<l>))  # set the foreground to the register closest to a grayscale value
4332	 * (L<width>)  # set the line width (RLogin extension)
4333	 * (M<pixel vector multiplier>)  # set the multiplication factor
4334	 * (N<setting>)  # 0 == negative patterns disabled, 1 == negative patterns enabled
4335	 * (P<pattern number>)  # 0..9: 0 == none, 1 == solid, 2 == 50% dash, 3 == dash-dot
4336	 * (P<pattern bits>)  # 2 to 8 bits represented as a 0/1 sequence
4337	 * (P<(M<pattern multiplier>))
4338	 * (R)  # replacement writing mode
4339	 * (S'<character>')  # set shading character
4340	 * (S<setting>)  # 0 == disable shding, 1 == enable shading
4341	 * (S[reference point])  # set a horizontal reference line including this point
4342	 * (S(X)[reference point])  # set a vertical reference line including this point
4343	 * (V)  # overlay writing mode
4344	 */
4345	TRACE(("found ReGIS command \"%c\" (write parameters)\n", ch));
4346	state->command = 'w';
4347	break;
4348    case '@':
4349	/* Macrograph */
4350	TRACE(("found ReGIS macrograph command\n"));
4351	ch = pop_fragment(&state->input);
4352	TRACE(("inspecting macrograph character \"%c\"\n", ch));
4353	switch (ch) {
4354	case '.':
4355	    TRACE(("clearing all macrographs FIXME\n"));
4356	    /* FIXME: handle */
4357	    break;
4358	case ':':
4359	    TRACE(("defining macrograph FIXME\n"));
4360	    /* FIXME: parse, handle  :<name> */
4361	    break;
4362	case ';':
4363	    TRACE(("DATA_ERROR: found extraneous terminator for macrograph definition\n"));
4364	    break;
4365	default:
4366	    if ((ch > 'A' && ch < 'Z') || (ch > 'a' && ch < 'z')) {
4367		TRACE(("expanding macrograph \"%c\" FIXME\n", ch));
4368		/* FIXME: handle */
4369	    } else {
4370		TRACE(("DATA_ERROR: unknown macrograph subcommand \"%c\"\n",
4371		       ch));
4372	    }
4373	    /* FIXME: parse, handle */
4374	    break;
4375	}
4376	break;
4377    default:
4378	TRACE(("DATA_ERROR: unknown ReGIS command %04x (%c), setting to '_'\n",
4379	       (int) ch, ch));
4380	state->command = '_';
4381	state->option = '_';
4382	return 0;
4383    }
4384
4385    state->option = '_';
4386
4387    return 1;
4388}
4389
4390static int
4391parse_regis_option(RegisParseState *state, RegisGraphicsContext *context)
4392{
4393    RegisDataFragment optionarg;
4394
4395    if (!extract_regis_option(&state->input, &state->option, &optionarg))
4396	return 0;
4397    skip_regis_whitespace(&optionarg);
4398
4399    TRACE(("found ReGIS option \"%c\": \"%s\"\n",
4400	   state->option, fragment_to_tempstr(&optionarg)));
4401
4402    switch (state->command) {
4403    case 'c':
4404	TRACE(("inspecting curve option \"%c\" with value \"%s\"\n",
4405	       state->option, fragment_to_tempstr(&optionarg)));
4406	switch (state->option) {
4407	case 'A':
4408	case 'a':
4409	    TRACE(("found arc length \"%s\"\n",
4410		   fragment_to_tempstr(&optionarg)));
4411	    {
4412		RegisDataFragment arclen;
4413
4414		if (!extract_regis_num(&optionarg, &arclen)) {
4415		    TRACE(("DATA_ERROR: expected int in curve arclen option: \"%s\"\n",
4416			   fragment_to_tempstr(&optionarg)));
4417		    break;
4418		}
4419		TRACE(("arc length string %s\n", fragment_to_tempstr(&arclen)));
4420		if (!regis_num_to_int(&arclen, &state->arclen)) {
4421		    TRACE(("DATA_ERROR: unable to parse int in curve arclen option: \"%s\"\n",
4422			   fragment_to_tempstr(&arclen)));
4423		    break;
4424		}
4425		TRACE(("value of arc length is %d\n", state->arclen));
4426		while (state->arclen < -360)
4427		    state->arclen += 360;
4428		while (state->arclen > 360)
4429		    state->arclen -= 360;
4430		TRACE(("using final arc length %d\n", state->arclen));
4431
4432		if (fragment_len(&optionarg)) {
4433		    TRACE(("DATA_ERROR: ignoring trailing junk in arc length option \"%s\"\n",
4434			   fragment_to_tempstr(&optionarg)));
4435		    break;
4436		}
4437	    }
4438	    break;
4439	case 'B':
4440	case 'b':
4441	    TRACE(("begin closed curve \"%s\"\n",
4442		   fragment_to_tempstr(&optionarg)));
4443	    if (fragment_len(&optionarg)) {
4444		TRACE(("DATA_ERROR: invalid closed curve option \"%s\"\n",
4445		       fragment_to_tempstr(&optionarg)));
4446		break;
4447	    }
4448	    state->curve_mode = CURVE_POSITION_CLOSED_CURVE;
4449	    state->num_points = 0U;
4450	    state->x_points[state->num_points] =
4451		context->graphics_output_cursor_x;
4452	    state->y_points[state->num_points] =
4453		context->graphics_output_cursor_y;
4454	    state->num_points++;
4455	    break;
4456	case 'C':
4457	case 'c':
4458	    TRACE(("found center position mode \"%s\"\n",
4459		   fragment_to_tempstr(&optionarg)));
4460	    if (fragment_len(&optionarg)) {
4461		TRACE(("DATA_ERROR: invalid center position option \"%s\"\n",
4462		       fragment_to_tempstr(&optionarg)));
4463		break;
4464	    }
4465	    state->curve_mode = CURVE_POSITION_ARC_CENTER;
4466	    break;
4467	case 'E':
4468	case 'e':
4469	    TRACE(("end curve \"%s\"\n", fragment_to_tempstr(&optionarg)));
4470	    switch (state->curve_mode) {
4471	    case CURVE_POSITION_CLOSED_CURVE:
4472		{
4473		    unsigned i;
4474
4475#ifdef DEBUG_SPLINE_POINTS
4476		    printf("points: \n");
4477		    for (i = 0; i < state->num_points; i++)
4478			printf("  %d,%d\n",
4479			       state->x_points[i], state->y_points[i]);
4480#endif
4481
4482#ifdef DEBUG_SPLINE_WITH_ROTATION
4483		    {
4484			static unsigned shift = 0;
4485			int temp_x[MAX_CURVE_POINTS], temp_y[MAX_CURVE_POINTS];
4486			shift++;
4487			shift = shift % state->num_points;
4488			for (i = 0; i < state->num_points; i++) {
4489			    temp_x[i] = state->x_points[i];
4490			    temp_y[i] = state->y_points[i];
4491			}
4492			for (i = 0; i < state->num_points; i++) {
4493			    state->x_points[i] =
4494				temp_x[(i + shift) % state->num_points];
4495			    state->y_points[i] =
4496				temp_y[(i + shift) % state->num_points];
4497			}
4498
4499#ifdef DEBUG_SPLINE_POINTS
4500			printf("after shift %d: \n", shift);
4501			for (i = 0; i < state->num_points; i++)
4502			    printf("  %d,%d\n",
4503				   state->x_points[i], state->y_points[i]);
4504#endif
4505		    }
4506#endif
4507
4508		    for (i = state->num_points; i > 0; i--) {
4509			state->x_points[i] = state->x_points[i - 1];
4510			state->y_points[i] = state->y_points[i - 1];
4511		    }
4512		    state->x_points[0] = state->x_points[state->num_points];
4513		    state->y_points[0] = state->y_points[state->num_points];
4514		    state->num_points++;
4515		    for (i = state->num_points; i != 0; i--) {
4516			state->x_points[i] = state->x_points[i - 1];
4517			state->y_points[i] = state->y_points[i - 1];
4518		    }
4519		    state->x_points[0] = state->x_points[state->num_points - 1];
4520		    state->y_points[0] = state->y_points[state->num_points - 1];
4521		    state->num_points++;
4522		    state->x_points[state->num_points] = state->x_points[2];
4523		    state->y_points[state->num_points] = state->y_points[2];
4524		    state->num_points++;
4525#ifdef DEBUG_SPLINE_WITH_OVERDRAW
4526		    state->x_points[state->num_points] = state->x_points[3];
4527		    state->y_points[state->num_points] = state->y_points[3];
4528		    state->num_points++;
4529		    state->x_points[state->num_points] = state->x_points[4];
4530		    state->y_points[state->num_points] = state->y_points[4];
4531		    state->num_points++;
4532#endif
4533#ifdef DEBUG_SPLINE_POINTS
4534		    printf("after points added: \n");
4535		    for (i = 0; i < state->num_points; i++)
4536			printf("  %d,%d\n",
4537			       state->x_points[i], state->y_points[i]);
4538#endif
4539		}
4540		TRACE(("drawing closed spline\n"));
4541		global_context = context;	/* FIXME: remove after updating spline code */
4542		plotCubicSpline((int) state->num_points - 1,
4543				state->x_points, state->y_points,
4544				1);
4545		break;
4546	    case CURVE_POSITION_OPEN_CURVE:
4547		TRACE(("drawing open spline\n"));
4548#ifdef DEBUG_SPLINE_POINTS
4549		{
4550		    int i;
4551
4552		    printf("points: \n");
4553		    for (i = 0; i < (int) state->num_points; i++)
4554			printf("  %d,%d\n",
4555			       state->x_points[i], state->y_points[i]);
4556		}
4557#endif
4558		global_context = context;	/* FIXME: remove after updating spline code */
4559		plotCubicSpline((int) state->num_points - 1,
4560				state->x_points, state->y_points,
4561				1);
4562		break;
4563	    default:
4564		TRACE(("DATA_ERROR: end curve option unexpected \"%s\"\n",
4565		       fragment_to_tempstr(&optionarg)));
4566		break;
4567	    }
4568	    break;
4569	case 'S':
4570	case 's':
4571	    TRACE(("begin open curve \"%s\"\n",
4572		   fragment_to_tempstr(&optionarg)));
4573	    if (fragment_len(&optionarg)) {
4574		TRACE(("DATA_ERROR: invalid open curve option \"%s\"\n",
4575		       fragment_to_tempstr(&optionarg)));
4576		break;
4577	    }
4578	    state->curve_mode = CURVE_POSITION_OPEN_CURVE;
4579	    state->num_points = 0U;
4580	    state->x_points[state->num_points] =
4581		context->graphics_output_cursor_x;
4582	    state->y_points[state->num_points] =
4583		context->graphics_output_cursor_y;
4584	    state->num_points++;
4585	    TRACE(("first point on curve with location %d,%d\n",
4586		   context->graphics_output_cursor_x,
4587		   context->graphics_output_cursor_y));
4588	    break;
4589	case 'W':
4590	case 'w':
4591	    TRACE(("found temporary write options \"%s\"\n",
4592		   fragment_to_tempstr(&optionarg)));
4593	    if (!load_regis_write_control_set(state, context,
4594					      context->graphics_output_cursor_x,
4595					      context->graphics_output_cursor_y,
4596					      &optionarg,
4597					      &context->temporary_write_controls)) {
4598		TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n",
4599		       fragment_to_tempstr(&optionarg)));
4600		break;
4601	    }
4602	    break;
4603	default:
4604	    TRACE(("DATA_ERROR: ignoring unknown ReGIS curve command option '%c' arg \"%s\"\n",
4605		   state->option, fragment_to_tempstr(&optionarg)));
4606	    break;
4607	}
4608	break;
4609    case 'f':
4610	TRACE(("ERROR: fill commands should not be handled here\n"));
4611	break;
4612    case 'l':
4613	TRACE(("inspecting load option \"%c\" with value \"%s\"\n",
4614	       state->option, fragment_to_tempstr(&optionarg)));
4615	switch (state->option) {
4616	case 'A':
4617	case 'a':
4618	    TRACE(("found alphabet specifier option \"%s\"\n",
4619		   fragment_to_tempstr(&optionarg)));
4620	    for (;;) {
4621		RegisDataFragment alphabetarg;
4622
4623		if (extract_regis_num(&optionarg, &alphabetarg)) {
4624		    int alphabet;
4625
4626		    TRACE(("alphabet number: %s\n",
4627			   fragment_to_tempstr(&alphabetarg)));
4628		    if (!regis_num_to_int(&alphabetarg, &alphabet)) {
4629			TRACE(("DATA_ERROR: unable to parse int in load alphabet option: \"%s\"\n",
4630			       fragment_to_tempstr(&alphabetarg)));
4631			break;
4632		    }
4633		    if (alphabet < 0 ||
4634			(unsigned) alphabet >= MAX_REGIS_ALPHABETS) {
4635			TRACE(("DATA_ERROR: invalid alphabet: \"%d\"\n",
4636			       alphabet));
4637			break;
4638		    }
4639#ifndef ENABLE_UPLOAD_ALPHABET_ZERO
4640		    if (alphabet == 0) {
4641			TRACE(("DATA_ERROR: alphabet 0 can not be modified\n"));
4642			break;
4643		    }
4644#endif
4645
4646		    TRACE(("using alphabet: %d\n", alphabet));
4647		    state->load_alphabet = (unsigned) alphabet;
4648		} else if (extract_regis_string(&optionarg, state->temp,
4649						state->templen)) {
4650		    TRACE(("alphabet name: %s\n",
4651			   fragment_to_tempstr(&alphabetarg)));
4652		    if (strlen(state->temp) == 0U ||
4653			strlen(state->temp) >= REGIS_ALPHABET_NAME_LEN) {
4654			TRACE(("DATA_ERROR: alphabet names must be between 1 and %u characters long: \"%s\" FIXME\n",
4655			       REGIS_ALPHABET_NAME_LEN - 1U, state->temp));
4656			break;
4657		    }
4658
4659		    strcpy(state->load_name, state->temp);
4660		    TRACE(("using name for alphabet %u: %s\n",
4661			   state->load_alphabet, state->load_name));
4662		} else if (skip_regis_whitespace(&optionarg)) {
4663		    ;
4664		} else if (!fragment_len(&optionarg)) {
4665		    break;
4666		} else {
4667		    TRACE(("DATA_ERROR: expected int or string in load alphabet option: \"%s\"\n",
4668			   fragment_to_tempstr(&optionarg)));
4669		    break;
4670		}
4671	    }
4672	    break;
4673#ifdef ENABLE_UPLOAD_ALPHABET_FROM_FONT
4674	case 'F':
4675	case 'f':
4676	    TRACE(("found font option \"%s\"\n",
4677		   fragment_to_tempstr(&optionarg)));
4678
4679	    if (state->load_index == MAX_REGIS_ALPHABETS) {
4680		state->load_index = find_free_alphabet_index(context,
4681							     state->load_alphabet,
4682							     state->load_w,
4683							     state->load_h);
4684		TRACE(("current alphabet is %u and size is %ux%u; assigning alphabet index %u\n",
4685		       state->load_alphabet, state->load_w, state->load_h, state->load_index));
4686	    }
4687
4688	    for (;;) {
4689		RegisDataFragment fontarg;
4690
4691		if (skip_regis_whitespace(&optionarg))
4692		    continue;
4693		if (extract_regis_num(&optionarg, &fontarg)) {
4694		    int enabled;
4695
4696		    TRACE(("fontname enabled: %s\n", fragment_to_tempstr(&fontarg)));
4697		    if (!regis_num_to_int(&fontarg, &enabled)) {
4698			TRACE(("DATA_ERROR: unable to parse int in load fontname option: \"%s\"\n",
4699			       fragment_to_tempstr(&fontarg)));
4700			break;
4701		    }
4702		    if (enabled != 0U && enabled != 1U) {
4703			TRACE(("DATA_ERROR: invalid fontname enable state: \"%d\"\n", enabled));
4704			break;
4705		    }
4706
4707		    TRACE(("fontname enabled: %d\n", enabled));
4708		    context->alphabets[state->load_index].use_font = enabled;
4709		    continue;
4710		}
4711		if (extract_regis_string(&optionarg, state->temp,
4712					 state->templen)) {
4713		    if (strlen(state->temp) == 0U ||
4714			strlen(state->temp) >= REGIS_FONTNAME_LEN) {
4715			TRACE(("DATA_ERROR: font names must be between 1 and %u characters long: \"%s\"\n",
4716			       REGIS_FONTNAME_LEN - 1U, state->temp));
4717			break;
4718		    }
4719
4720		    strcpy(context->alphabets[state->load_index].fontname,
4721			   state->temp);
4722		    context->alphabets[state->load_index].use_font = 1;
4723		    TRACE(("using backing font: %s\n",
4724			   context->alphabets[state->load_index].fontname));
4725		}
4726
4727		if (!fragment_len(&optionarg)) {
4728		    break;
4729		} else {
4730		    TRACE(("DATA_ERROR: unexpected text in load fontname option: \"%s\"\n",
4731			   fragment_to_tempstr(&optionarg)));
4732		    break;
4733		}
4734	    }
4735	    break;
4736#endif
4737#ifdef ENABLE_USER_FONT_SIZE
4738	case 'S':
4739	case 's':
4740	    TRACE(("found glyph size option \"%s\"\n",
4741		   fragment_to_tempstr(&optionarg)));
4742	    while (fragment_len(&optionarg) > 0) {
4743		RegisDataFragment sizearg;
4744
4745		if (skip_regis_whitespace(&optionarg))
4746		    continue;
4747
4748		if (extract_regis_extent(&optionarg, &sizearg)) {
4749		    int w, h;
4750		    unsigned size;
4751
4752		    TRACE(("glyph size: %s\n",
4753			   fragment_to_tempstr(&sizearg)));
4754		    /* FIXME: verify this is in pixels, not user coordinates */
4755		    if (!load_regis_pixel_extent(fragment_to_tempstr(&sizearg),
4756						 0, 0,
4757						 &w, &h)) {
4758			TRACE(("DATA_ERROR: unable to parse extent in glyph size option: \"%s\"\n",
4759			       fragment_to_tempstr(&sizearg)));
4760			break;
4761		    }
4762		    if (w < 1 || h < 1) {
4763			TRACE(("DATA_ERROR: glyph dimensions must not be negative or zero: %dx%d\n",
4764			       w, h));
4765			break;
4766		    }
4767		    size = GLYPH_WIDTH_BYTES((unsigned) w) * (unsigned) h;
4768		    if (size > MAX_REGIS_ALPHABET_BYTES) {
4769			TRACE(("DATA_ERROR: glyph is too large (%u bytes, limit is %u bytes)\n",
4770			       size, MAX_REGIS_ALPHABET_BYTES));
4771			break;
4772		    }
4773
4774		    if (state->load_index != MAX_REGIS_ALPHABETS) {
4775			TRACE(("DATA_ERROR: glyph size can not be changed after any data is loaded\n"));
4776			break;
4777		    }
4778
4779		    TRACE(("using glyph size: %dx%d\n", w, h));
4780		    state->load_w = (unsigned) w;
4781		    state->load_h = (unsigned) h;
4782		    continue;
4783		}
4784
4785		TRACE(("DATA_ERROR: expected extent in glyph size option: \"%s\"\n",
4786		       fragment_to_tempstr(&sizearg)));
4787		break;
4788	    }
4789	    break;
4790#endif
4791	default:
4792	    TRACE(("DATA_ERROR: ignoring unknown ReGIS load command option '%c' arg \"%s\"\n",
4793		   state->option, fragment_to_tempstr(&optionarg)));
4794	    break;
4795	}
4796	break;
4797    case 'p':
4798	TRACE(("inspecting position option \"%c\" with value \"%s\"\n",
4799	       state->option, fragment_to_tempstr(&optionarg)));
4800	switch (state->option) {
4801	case 'B':
4802	case 'b':
4803	    TRACE(("found begin bounded position stack \"%s\"\n",
4804		   fragment_to_tempstr(&optionarg)));
4805	    skip_regis_whitespace(&optionarg);
4806	    if (fragment_len(&optionarg) > 0U) {
4807		TRACE(("DATA_ERROR: ignoring unexpected arguments to end position option '%c' arg \"%s\"\n",
4808		       state->option, fragment_to_tempstr(&optionarg)));
4809	    }
4810	    if (state->stack_next >= POSITION_STACK_SIZE) {
4811		/* FIXME: ignore, error, update counter? */
4812		TRACE(("unable to push position to full stack\n"));
4813		break;
4814	    }
4815
4816	    state->stack_x[state->stack_next] =
4817		context->graphics_output_cursor_x;
4818	    state->stack_y[state->stack_next] =
4819		context->graphics_output_cursor_y;
4820	    state->stack_next++;
4821	    break;
4822	case 'E':
4823	case 'e':
4824	    TRACE(("found end position stack \"%s\"\n",
4825		   fragment_to_tempstr(&optionarg)));
4826	    skip_regis_whitespace(&optionarg);
4827	    if (fragment_len(&optionarg) > 0U) {
4828		TRACE(("DATA_ERROR: ignoring unexpected arguments to end position option '%c' arg \"%s\"\n",
4829		       state->option, fragment_to_tempstr(&optionarg)));
4830	    }
4831	    if (state->stack_next == 0U) {
4832		TRACE(("DATA_ERROR: unable to pop position from empty stack\n"));
4833		break;
4834	    }
4835
4836	    state->stack_next--;
4837	    if (state->stack_x[state->stack_next] != DUMMY_STACK_X ||
4838		state->stack_y[state->stack_next] != DUMMY_STACK_Y) {
4839		context->graphics_output_cursor_x =
4840		    state->stack_x[state->stack_next];
4841		context->graphics_output_cursor_y =
4842		    state->stack_y[state->stack_next];
4843	    }
4844	    break;
4845	case 'P':
4846	case 'p':
4847	    TRACE(("found graphics page \"%s\" FIXME\n",
4848		   fragment_to_tempstr(&optionarg)));
4849	    /* FIXME: handle */
4850	    break;
4851	case 'S':
4852	case 's':
4853	    TRACE(("found begin unbounded position stack \"%s\"\n",
4854		   fragment_to_tempstr(&optionarg)));
4855	    skip_regis_whitespace(&optionarg);
4856	    if (fragment_len(&optionarg) > 0U) {
4857		TRACE(("DATA_ERROR: ignoring unexpected arguments to end position option '%c' arg \"%s\"\n",
4858		       state->option, fragment_to_tempstr(&optionarg)));
4859	    }
4860	    if (state->stack_next >= POSITION_STACK_SIZE) {
4861		/* FIXME: ignore, error, update counter? */
4862		TRACE(("unable to push dummy position to full stack\n"));
4863		break;
4864	    }
4865
4866	    state->stack_x[state->stack_next] = DUMMY_STACK_X;
4867	    state->stack_y[state->stack_next] = DUMMY_STACK_Y;
4868	    state->stack_next++;
4869	    break;
4870	case 'W':
4871	case 'w':
4872	    TRACE(("found temporary write options \"%s\"\n",
4873		   fragment_to_tempstr(&optionarg)));
4874	    if (!load_regis_write_control_set(state, context,
4875					      context->graphics_output_cursor_x,
4876					      context->graphics_output_cursor_y,
4877					      &optionarg,
4878					      &context->temporary_write_controls)) {
4879		TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n",
4880		       fragment_to_tempstr(&optionarg)));
4881	    }
4882	    break;
4883	default:
4884	    TRACE(("DATA_ERROR: ignoring unknown ReGIS position command option '%c' arg \"%s\"\n",
4885		   state->option, fragment_to_tempstr(&optionarg)));
4886	    break;
4887	}
4888	break;
4889    case 'r':
4890	TRACE(("inspecting report option \"%c\" with value \"%s\"\n",
4891	       state->option, fragment_to_tempstr(&optionarg)));
4892	switch (state->option) {
4893	case 'E':
4894	case 'e':
4895	    TRACE(("found parse error report \"%s\" FIXME\n",
4896		   fragment_to_tempstr(&optionarg)));
4897	    skip_regis_whitespace(&optionarg);
4898	    if (fragment_len(&optionarg) > 0U) {
4899		TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n",
4900		       state->option, fragment_to_tempstr(&optionarg)));
4901		break;
4902	    } {
4903		char reply[64];
4904
4905		TRACE(("got report last error condition\n"));
4906		/* FIXME: verify no CSI */
4907		/* FIXME: implement after adding error tracking */
4908		sprintf(reply, "\"%u, %u\"\r", 0U, 0U);
4909		unparseputs(context->graphic->xw, reply);
4910		unparse_end(context->graphic->xw);
4911	    }
4912	    break;
4913	case 'I':
4914	case 'i':
4915	    TRACE(("found set input mode \"%s\"\n",
4916		   fragment_to_tempstr(&optionarg)));
4917	    {
4918		RegisDataFragment modearg;
4919		int mode;
4920
4921		if (!extract_regis_num(&optionarg, &modearg)) {
4922		    TRACE(("DATA_ERROR: expected int in report input mode option: \"%s\"\n",
4923			   fragment_to_tempstr(&modearg)));
4924		    break;
4925		}
4926
4927		TRACE(("input mode: %s\n", fragment_to_tempstr(&modearg)));
4928		if (!regis_num_to_int(&modearg, &mode)) {
4929		    TRACE(("DATA_ERROR: unable to parse int in report input mode option: \"%s\"\n",
4930			   fragment_to_tempstr(&modearg)));
4931		    break;
4932		}
4933		if (mode != 0 && mode != 1) {
4934		    TRACE(("DATA_ERROR: ignoring invalid input mode: \"%d\"\n",
4935			   mode));
4936		    break;
4937		}
4938
4939		TRACE(("using input mode: %d\n", mode));
4940		context->multi_input_mode = mode;
4941		if (context->multi_input_mode) {
4942		    TRACE(("ERROR: multi-mode input not implemented FIXME\n"));
4943		    /* enable input cursor, send location on mouse clicks */
4944		} else {
4945		    /* disable input cursor, don't track mouse clicks */
4946		}
4947
4948		unparseputs(context->graphic->xw, "\r");
4949		unparse_end(context->graphic->xw);
4950
4951		skip_regis_whitespace(&optionarg);
4952		if (fragment_len(&optionarg) > 0U) {
4953		    TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n",
4954			   state->option, fragment_to_tempstr(&optionarg)));
4955		}
4956	    }
4957	    break;
4958	case 'L':
4959	case 'l':
4960	    TRACE(("found character set load report \"%s\"\n",
4961		   fragment_to_tempstr(&optionarg)));
4962	    if (fragment_len(&optionarg) > 0U) {
4963		TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n",
4964		       state->option, fragment_to_tempstr(&optionarg)));
4965		break;
4966	    }
4967	    if (state->load_index == MAX_REGIS_ALPHABETS) {
4968		TRACE(("DATA_ERROR: unable to report alphabet name because no alphabet is loading\n"));
4969		break;
4970	    }
4971
4972	    /* FIXME: also send CSI here? */
4973	    unparseputs(context->graphic->xw, "\033A'");
4974	    unparseputs(context->graphic->xw,
4975			context->alphabets[state->load_index].name);
4976	    unparseputs(context->graphic->xw, "'\r");
4977	    unparse_end(context->graphic->xw);
4978	    break;
4979	case 'M':
4980	case 'm':
4981	    TRACE(("found macrograph report \"%s\"\n",
4982		   fragment_to_tempstr(&optionarg)));
4983	    {
4984		RegisDataFragment suboptionarg;
4985		char name = '\0';
4986
4987		if (extract_regis_parenthesized_data(&optionarg,
4988						     &suboptionarg)) {
4989		    skip_regis_whitespace(&suboptionarg);
4990		    TRACE(("got macrograph report character: \"%s\"\n",
4991			   fragment_to_tempstr(&suboptionarg)));
4992		    if (fragment_len(&suboptionarg) > 0U) {
4993			name = pop_fragment(&suboptionarg);
4994
4995			skip_regis_whitespace(&suboptionarg);
4996			if (fragment_len(&optionarg) > 0U) {
4997			    TRACE(("DATA_ERROR: unexpected content in ReGIS macrograph report suboptions: \"%s\"\n",
4998				   fragment_to_tempstr(&suboptionarg)));
4999			    break;
5000			}
5001		    }
5002		}
5003		skip_regis_whitespace(&optionarg);
5004		if (fragment_len(&optionarg) > 0U) {
5005		    TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n",
5006			   state->option, fragment_to_tempstr(&optionarg)));
5007		    break;
5008		}
5009
5010		if (name == '\0') {
5011		    TRACE(("DATA_ERROR: no macro name given to ReGIS macrograph report command\n"));
5012		    break;
5013		}
5014
5015		if (name == '=') {
5016		    char reply[64];
5017
5018		    TRACE(("got report macrograph storage\n"));
5019		    /* FIXME: verify no CSI */
5020		    /* FIXME: implement after adding macros */
5021		    sprintf(reply, "\"%u, %u\"\r", 1000U, 1000U);
5022		    unparseputs(context->graphic->xw, reply);
5023		    unparse_end(context->graphic->xw);
5024		} else {
5025		    TRACE(("got report macrograph name '%c'\n", name));
5026		    /* FIXME: implement when macrographs are supported (and allow it to be disabled) */
5027		    /* FIXME: also send CSI here? */
5028		    unparseputs(context->graphic->xw, "@;\r");
5029		    unparse_end(context->graphic->xw);
5030		}
5031	    }
5032	    break;
5033	case 'P':
5034	case 'p':
5035	    TRACE(("found cursor position report \"%s\"\n",
5036		   fragment_to_tempstr(&optionarg)));
5037	    {
5038		RegisDataFragment suboptionarg;
5039		int output = 1;
5040
5041		if (extract_regis_parenthesized_data(&optionarg,
5042						     &suboptionarg)) {
5043		    skip_regis_whitespace(&suboptionarg);
5044		    TRACE(("got cursor position report suboption: \"%s\"\n",
5045			   fragment_to_tempstr(&suboptionarg)));
5046		    if (fragment_len(&suboptionarg) > 0U) {
5047			char suboption;
5048
5049			suboption = pop_fragment(&suboptionarg);
5050			if (suboption == 'i' || suboption == 'I') {
5051			    output = 0;		/* input location report */
5052			} else {
5053			    TRACE(("DATA_ERROR: unknown ReGIS postion report suboption '%c'\n",
5054				   suboption));
5055			    break;
5056			}
5057
5058			skip_regis_whitespace(&suboptionarg);
5059			if (fragment_len(&optionarg) > 0U) {
5060			    TRACE(("DATA_ERROR: unexpected content in ReGIS postion report suboptions: \"%s\"\n",
5061				   fragment_to_tempstr(&suboptionarg)));
5062			    break;
5063			}
5064		    }
5065		}
5066		skip_regis_whitespace(&optionarg);
5067		if (fragment_len(&optionarg) > 0U) {
5068		    TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n",
5069			   state->option, fragment_to_tempstr(&optionarg)));
5070		    break;
5071		}
5072
5073		TRACE(("got report cursor position output=%d\n", output));
5074
5075		if (output == 1) {
5076		    char reply[64];
5077
5078		    /* FIXME: verify no leading char or button sequence */
5079		    /* FIXME: should we ever send and eight-bit CSI? */
5080		    sprintf(reply, "\033[[%d,%d]\r",
5081			    context->graphics_output_cursor_x,
5082			    context->graphics_output_cursor_y);
5083		    unparseputs(context->graphic->xw, reply);
5084		    unparse_end(context->graphic->xw);
5085		} else {
5086		    if (context->multi_input_mode) {
5087			/* send CSI240~[x,y]\r with current input cursor location */
5088			TRACE(("ERROR: multi-mode input report not implemented FIXME\n"));
5089			break;
5090		    } else {
5091			/* display input cursor, grab mouse and keyboard, use arrow keys and shifted arrow keys to move cursor, and report on first keypress or mouse click */
5092			/* send <button sequence>[x,y]\r with input cursor location */
5093			TRACE(("ERROR: one shot input report not implemented FIXME\n"));
5094			break;
5095		    }
5096		}
5097	    }
5098	    break;
5099	default:
5100	    TRACE(("DATA_ERROR: ignoring unknown ReGIS report command option '%c' arg \"%s\"\n",
5101		   state->option, fragment_to_tempstr(&optionarg)));
5102	    break;
5103	}
5104	break;
5105    case 's':
5106	TRACE(("inspecting screen option \"%c\" with value \"%s\"\n",
5107	       state->option, fragment_to_tempstr(&optionarg)));
5108	switch (state->option) {
5109	case 'A':
5110	case 'a':
5111	    TRACE(("found address definition \"%s\"\n",
5112		   fragment_to_tempstr(&optionarg)));
5113	    {
5114		RegisDataFragment address_extent;
5115		int got_ul = 0;
5116		int got_lr = 0;
5117		int ulx = 0, uly = 0, lrx = 0, lry = 0;
5118
5119		while (fragment_len(&optionarg)) {
5120		    if (skip_regis_whitespace(&optionarg))
5121			continue;
5122
5123		    if (extract_regis_extent(&optionarg, &address_extent)) {
5124			int x, y;
5125
5126			/* FIXME: are relative values supposed to be handled? */
5127			if (!load_regis_pixel_extent(fragment_to_tempstr(&address_extent),
5128						     0, 0, &x, &y)) {
5129			    TRACE(("DATA_ERROR: unable to parse extent in address definition: \"%s\"\n",
5130				   fragment_to_tempstr(&address_extent)));
5131			    break;
5132			}
5133
5134			if (!got_ul) {
5135			    ulx = x;
5136			    uly = y;
5137			    got_ul = 1;
5138			} else if (!got_lr) {
5139			    lrx = x;
5140			    lry = y;
5141			    got_lr = 1;
5142			} else {
5143			    TRACE(("DATA_ERROR: ignoring extra extent argument in address definition: \"%s\"\n",
5144				   fragment_to_tempstr(&address_extent)));
5145			}
5146			continue;
5147		    }
5148
5149		    TRACE(("DATA_ERROR: ignoring malformed ReGIS screen address definition: expected extent argument but found: \"%s\"\n",
5150			   fragment_to_tempstr(&optionarg)));
5151		    return 1;
5152		}
5153
5154		if (!got_ul || !got_lr) {
5155		    TRACE(("DATA_ERROR: ignoring malformed ReGIS screen address definition: one or both locations missing in definition\n"));
5156		    return 1;
5157		}
5158		if (ulx == lrx || uly == lry) {
5159		    TRACE(("DATA_ERROR: ignoring malformed ReGIS screen address definition: one or both dimensions are zero: ul=%d,%d lr=%d,%d\n",
5160			   ulx, uly, lrx, lry));
5161		    return 1;
5162		} {
5163		    const int cw = abs(ulx - lrx) + 1;
5164		    const int ch = abs(uly - lry) + 1;
5165		    int scale;
5166		    int width, height;
5167
5168		    width = cw;
5169		    height = ch;
5170
5171		    scale = 1;
5172		    while (width * scale < 200 ||
5173			   height * scale < 200) {
5174			scale++;
5175		    }
5176		    width *= scale;
5177		    height *= scale;
5178
5179		    scale = 1;
5180		    while (width / scale > context->graphic->max_width ||
5181			   height / scale > context->graphic->max_height) {
5182			scale++;
5183		    }
5184		    width /= scale;
5185		    height /= scale;
5186
5187		    TRACE(("custom screen address: ul=%d,%d lr=%d,%d\n",
5188			   ulx, uly, lrx, lry));
5189
5190		    context->x_off = ulx;
5191		    context->y_off = uly;
5192		    context->x_div = lrx - ulx;
5193		    context->y_div = lry - uly;
5194		    context->width = width;
5195		    context->height = height;
5196		    context->graphic->actual_width = width;
5197		    context->graphic->actual_height = height;
5198
5199		    TRACE(("conversion factors: off=%+d,%+d div=%+d,%+d width=%d, height=%d\n",
5200			   context->x_off, context->y_off,
5201			   context->x_div, context->y_div,
5202			   context->width, context->height));
5203		}
5204	    }
5205	    break;
5206	case 'C':
5207	case 'c':
5208	    TRACE(("found cursor control \"%s\" FIXME\n",
5209		   fragment_to_tempstr(&optionarg)));
5210	    /* FIXME: handle */
5211	    if (!fragment_len(&optionarg)) {
5212		TRACE(("DATA_ERROR: ignoring malformed ReGIS screen cursor control option value \"%s\"\n",
5213		       fragment_to_tempstr(&optionarg)));
5214		return 1;
5215	    }
5216	    break;
5217	case 'E':
5218	case 'e':
5219	    TRACE(("found erase request \"%s\"\n",
5220		   fragment_to_tempstr(&optionarg)));
5221	    if (fragment_len(&optionarg)) {
5222		TRACE(("DATA_ERROR: ignoring unexpected argument to ReGIS erase request \"%s\"\n",
5223		       fragment_to_tempstr(&optionarg)));
5224		return 1;
5225	    }
5226	    DRAW_ALL(context, context->background);
5227	    break;
5228	case 'F':
5229	case 'f':
5230	    TRACE(("found page eject request \"%s\"\n",
5231		   fragment_to_tempstr(&optionarg)));
5232	    if (fragment_len(&optionarg)) {
5233		TRACE(("DATA_ERROR: ignoring unexpected argument to ReGIS page eject request \"%s\"\n",
5234		       fragment_to_tempstr(&optionarg)));
5235		return 1;
5236	    }
5237	    /* We aren't going to print anything so no need to deduplicate. */
5238	    DRAW_ALL(context, context->background);
5239	    break;
5240	case 'H':
5241	case 'h':
5242	    TRACE(("found hardcopy control \"%s\" FIXME\n",
5243		   fragment_to_tempstr(&optionarg)));
5244	    /* FIXME: handle */
5245	    if (!fragment_len(&optionarg)) {
5246		TRACE(("DATA_ERROR: ignoring malformed ReGIS screen hardcopy control option value \"%s\"\n",
5247		       fragment_to_tempstr(&optionarg)));
5248		return 1;
5249	    }
5250	    break;
5251	case 'I':
5252	case 'i':
5253	    TRACE(("found screen background color index \"%s\"\n",
5254		   fragment_to_tempstr(&optionarg)));
5255	    if (!load_regis_regnum_or_colorspec(context, &optionarg,
5256						&context->background)) {
5257		TRACE(("DATA_ERROR: screen background color specifier not recognized: \"%s\"\n",
5258		       fragment_to_tempstr(&optionarg)));
5259		return 1;
5260	    }
5261	    break;
5262	case 'M':
5263	case 'm':
5264	    TRACE(("found screen color register mapping \"%s\"\n",
5265		   fragment_to_tempstr(&optionarg)));
5266	    {
5267		RegisDataFragment regnum;
5268		RegisDataFragment colorspec;
5269
5270		while (fragment_len(&optionarg)) {
5271		    if (skip_regis_whitespace(&optionarg))
5272			continue;
5273
5274		    if (extract_regis_num(&optionarg, &regnum)) {
5275			int register_num;
5276			int color_only;
5277			short r, g, b;
5278
5279			if (!regis_num_to_int(&regnum, &register_num)) {
5280			    TRACE(("DATA_ERROR: unable to parse int in screen color register mapping option: \"%s\"\n",
5281				   fragment_to_tempstr(&regnum)));
5282			    return 1;
5283			}
5284			if (register_num < 0 ||
5285			    register_num > (int) context->graphic->valid_registers) {
5286			    TRACE(("interpreting out of range register number %d as 0 FIXME\n",
5287				   register_num));
5288			    register_num = 0;
5289			}
5290			skip_regis_whitespace(&optionarg);
5291			if (!extract_regis_parenthesized_data(&optionarg,
5292							      &colorspec)) {
5293			    TRACE(("DATA_ERROR: expected to find parentheses after register number: \"%s\"\n",
5294				   fragment_to_tempstr(&optionarg)));
5295			    return 1;
5296			}
5297
5298			skip_regis_whitespace(&colorspec);
5299			switch (peek_fragment(&colorspec)) {
5300			case 'A':
5301			case 'a':
5302			    pop_fragment(&colorspec);
5303			    color_only = 1;
5304			    break;
5305			default:
5306			    color_only = 0;
5307			    break;
5308			}
5309
5310			TRACE(("mapping register %d to color spec: \"%s\"\n",
5311			       register_num, fragment_to_tempstr(&colorspec)));
5312			if (!load_regis_colorspec(context, &colorspec,
5313						  &r, &g, &b)) {
5314			    TRACE(("DATA_ERROR: unable to use colorspec for mapping of register %d\n",
5315				   register_num));
5316			    return 1;
5317			}
5318
5319			if (color_only &&
5320			    (context->terminal_id == 240 ||
5321			     context->terminal_id == 330)) {
5322			    TRACE(("NOT setting color register %d to %hd,%hd,%hd\n",
5323				   register_num, r, g, b));
5324			} else {
5325			    TRACE(("setting color register %d to %hd,%hd,%hd\n",
5326				   register_num, r, g, b));
5327			    update_color_register(context->graphic,
5328						  (RegisterNum) register_num,
5329						  r, g, b);
5330			}
5331			continue;
5332		    } {
5333			char skip;
5334
5335			skip_regis_whitespace(&optionarg);
5336			skip = pop_fragment(&optionarg);
5337			(void) skip;	/* variable needed only if tracing */
5338			TRACE(("DATA_ERROR: ignoring mapping request with unexpected character \"%c\"\n",
5339			       skip));
5340			return 1;
5341		    }
5342		}
5343	    }
5344	    break;
5345	case 'P':
5346	case 'p':
5347	    TRACE(("found graphics page display request \"%s\" FIXME\n",
5348		   fragment_to_tempstr(&optionarg)));
5349	    /* FIXME: handle */
5350	    if (!fragment_len(&optionarg)) {
5351		TRACE(("DATA_ERROR: ignoring malformed ReGIS screen graphics page display option value \"%s\"\n",
5352		       fragment_to_tempstr(&optionarg)));
5353		return 1;
5354	    }
5355	    break;
5356	case 'T':
5357	case 't':
5358	    TRACE(("found time delay \"%s\" FIXME\n",
5359		   fragment_to_tempstr(&optionarg)));
5360	    /* FIXME: handle */
5361	    if (!fragment_len(&optionarg)) {
5362		TRACE(("DATA_ERROR: ignoring malformed ReGIS screen time delay option value \"%s\"\n",
5363		       fragment_to_tempstr(&optionarg)));
5364		return 1;
5365	    }
5366	    break;
5367	case 'W':
5368	case 'w':
5369	    TRACE(("found PV \"%s\" FIXME\n", fragment_to_tempstr(&optionarg)));
5370	    /* FIXME: handle */
5371	    if (!fragment_len(&optionarg)) {
5372		TRACE(("DATA_ERROR: ignoring malformed ReGIS screen PV option value \"%s\"\n",
5373		       fragment_to_tempstr(&optionarg)));
5374		return 1;
5375	    }
5376	    break;
5377	default:
5378	    TRACE(("DATA_ERROR: ignoring unknown ReGIS screen command option '%c' arg \"%s\"\n",
5379		   state->option, fragment_to_tempstr(&optionarg)));
5380	    break;
5381	}
5382	break;
5383    case 't':
5384	TRACE(("inspecting text option \"%c\" with value \"%s\"\n",
5385	       state->option, fragment_to_tempstr(&optionarg)));
5386	if (!fragment_len(&optionarg)) {
5387	    TRACE(("DATA_ERROR: ignoring malformed ReGIS text command option value \"%s\"\n",
5388		   fragment_to_tempstr(&optionarg)));
5389	    return 1;
5390	}
5391	switch (state->option) {
5392	case 'A':
5393	case 'a':
5394	    TRACE(("found alphabet specifier option \"%s\"\n",
5395		   fragment_to_tempstr(&optionarg)));
5396	    {
5397		RegisDataFragment alphabetarg;
5398		int alphabet;
5399
5400		if (!extract_regis_num(&optionarg, &alphabetarg)) {
5401		    TRACE(("DATA_ERROR: expected int in text alphabet option: \"%s\"\n",
5402			   fragment_to_tempstr(&optionarg)));
5403		    break;
5404		}
5405		TRACE(("alphabet: %s\n", fragment_to_tempstr(&alphabetarg)));
5406		if (!regis_num_to_int(&alphabetarg, &alphabet)) {
5407		    TRACE(("DATA_ERROR: unable to parse int in text alphabet option: \"%s\"\n",
5408			   fragment_to_tempstr(&alphabetarg)));
5409		    break;
5410		}
5411		if (alphabet < 0 ||
5412		    (unsigned) alphabet >= MAX_REGIS_ALPHABETS) {
5413		    TRACE(("DATA_ERROR: invalid alphabet: \"%d\"\n", alphabet));
5414		    break;
5415		}
5416
5417		TRACE(("using alphabet number: %d\n", alphabet));
5418		context->current_text_controls->alphabet_num = (unsigned) alphabet;
5419
5420		if (fragment_len(&optionarg)) {
5421		    TRACE(("DATA_ERROR: ignoring trailing junk in text alphabet option \"%s\"\n",
5422			   fragment_to_tempstr(&alphabetarg)));
5423		    break;
5424		}
5425	    }
5426	    break;
5427	case 'B':
5428	case 'b':
5429	    TRACE(("found beginning of temporary text control \"%s\"\n",
5430		   fragment_to_tempstr(&optionarg)));
5431	    copy_regis_text_controls(&context->persistent_text_controls,
5432				     &context->temporary_text_controls);
5433	    context->current_text_controls = &context->temporary_text_controls;
5434	    break;
5435	case 'D':
5436	case 'd':
5437	    TRACE(("found text tilt control \"%s\"\n",
5438		   fragment_to_tempstr(&optionarg)));
5439	    {
5440		RegisDataFragment rotationarg;
5441		int rotation;
5442
5443		if (!extract_regis_num(&optionarg, &rotationarg)) {
5444		    TRACE(("DATA_ERROR: expected int in text tilt option: \"%s\"\n",
5445			   fragment_to_tempstr(&optionarg)));
5446		    break;
5447		}
5448		TRACE(("tilt: %s\n", fragment_to_tempstr(&rotationarg)));
5449		if (!regis_num_to_int(&rotationarg, &rotation)) {
5450		    TRACE(("DATA_ERROR: unable to parse int in text tilt option: \"%s\"\n",
5451			   fragment_to_tempstr(&rotationarg)));
5452		    break;
5453		}
5454		while (rotation < 0) {
5455		    rotation += 360;
5456		}
5457		while (rotation >= 360) {
5458		    rotation -= 360;
5459		}
5460		/* FIXME: we don't have to be this restrictive, though the
5461		 * VT3x0 apparently was. What might depend on this?
5462		 */
5463#ifndef ENABLE_FREE_ROTATION
5464		/* Use closest value which is a multiple of 45 degrees. */
5465		rotation = 45 * ((rotation + 22) / 45);
5466#endif
5467
5468		/* For some reason ReGIS reused the "D" option for the text
5469		 * command to represent two different attributes.  Character
5470		 * tilt can only be modified if a string tilt option has
5471		 * already been given.
5472		 */
5473		/* FIXME: handle character size prameter */
5474		if (state->string_rot_set) {
5475		    TRACE(("using character rotation (tilt): %d\n", rotation));
5476		    context->current_text_controls->character_rotation =
5477			rotation;
5478		} else {
5479		    TRACE(("using string rotation (tilt): %d\n", rotation));
5480		    context->current_text_controls->string_rotation =
5481			rotation;
5482		    context->current_text_controls->character_rotation =
5483			rotation;
5484		    state->string_rot_set = 1;
5485		}
5486
5487		if (fragment_len(&optionarg)) {
5488		    TRACE(("DATA_ERROR: ignoring trailing junk in text tilt option \"%s\"\n",
5489			   fragment_to_tempstr(&rotationarg)));
5490		    break;
5491		}
5492	    }
5493	    break;
5494	case 'E':
5495	case 'e':
5496	    TRACE(("found end of temporary text control \"%s\"\n",
5497		   fragment_to_tempstr(&optionarg)));
5498	    context->current_text_controls = &context->persistent_text_controls;
5499	    break;
5500	case 'H':
5501	case 'h':
5502	    TRACE(("found height multiplier \"%s\"\n",
5503		   fragment_to_tempstr(&optionarg)));
5504	    {
5505		RegisDataFragment multiarg;
5506		int multiplier;
5507		unsigned height;
5508
5509		if (!extract_regis_num(&optionarg, &multiarg)) {
5510		    TRACE(("DATA_ERROR: expected int in text height multiplier option: \"%s\"\n",
5511			   fragment_to_tempstr(&optionarg)));
5512		    break;
5513		}
5514		TRACE(("multiplier: %s\n", fragment_to_tempstr(&multiarg)));
5515		if (!regis_num_to_int(&multiarg, &multiplier)) {
5516		    TRACE(("DATA_ERROR: unable to parse int in text height multiplier option: \"%s\"\n",
5517			   fragment_to_tempstr(&multiarg)));
5518		    break;
5519		}
5520		if (multiplier < 0) {
5521		    TRACE(("DATA_ERROR: out of range height multiplier: \"%d\", using 0 FIXME\n",
5522			   multiplier));
5523		    multiplier = 0;	/* FIXME: verify zero is accepted */
5524		}
5525		if (multiplier > 256) {
5526		    TRACE(("DATA_ERROR: out of range height multiplier: \"%d\", using 256 FIXME\n",
5527			   multiplier));
5528		    multiplier = 256;
5529		}
5530		TRACE(("using height multiplier: %d\n", multiplier));
5531		height = (unsigned) multiplier *10U;	/* base character height */
5532		context->current_text_controls->character_display_h = height;
5533		context->current_text_controls->character_unit_cell_h = height;
5534
5535		if (fragment_len(&optionarg)) {
5536		    TRACE(("DATA_ERROR: ignoring trailing junk in text multiplier option \"%s\"\n",
5537			   fragment_to_tempstr(&multiarg)));
5538		    break;
5539		}
5540	    }
5541	    break;
5542	case 'I':
5543	case 'i':
5544	    TRACE(("found italic control \"%s\"\n",
5545		   fragment_to_tempstr(&optionarg)));
5546	    {
5547		RegisDataFragment italicarg;
5548		int italic;
5549
5550		if (!extract_regis_num(&optionarg, &italicarg)) {
5551		    TRACE(("DATA_ERROR: expected int in text italic option: \"%s\"\n",
5552			   fragment_to_tempstr(&optionarg)));
5553		    break;
5554		}
5555		TRACE(("italic angle: %s\n", fragment_to_tempstr(&italicarg)));
5556		if (!regis_num_to_int(&italicarg, &italic)) {
5557		    TRACE(("DATA_ERROR: unable to parse int in text italic option: \"%s\"\n",
5558			   fragment_to_tempstr(&italicarg)));
5559		    break;
5560		}
5561
5562		/*
5563		 * This is overly-restrictive but matches what the docs say
5564		 * should happen.  Add an option to allow exact angles?
5565		 */
5566#ifndef ENABLE_VARIABLE_ITALICS
5567		if (italic <= -31) {
5568		    italic = -45;
5569		} else if (italic < 0) {
5570		    italic = -27;	/* docs say 22, but that gives .404 x:y ratio */
5571		} else if (italic >= 31) {
5572		    italic = 45;
5573		} else if (italic > 0) {
5574		    italic = 27;	/* docs say 22, but that gives .404 x:y ratio */
5575		}
5576#else
5577		if (italic <= -72) {
5578		    italic = -72;
5579		} else if (italic >= 72) {
5580		    italic = 72;
5581		}
5582#endif
5583
5584		TRACE(("using italic angle: %d\n", italic));
5585		context->current_text_controls->slant = italic;
5586
5587		if (fragment_len(&optionarg)) {
5588		    TRACE(("DATA_ERROR: ignoring trailing junk in text italic option \"%s\"\n",
5589			   fragment_to_tempstr(&italicarg)));
5590		    break;
5591		}
5592	    }
5593	    break;
5594	case 'M':
5595	case 'm':
5596	    TRACE(("found text command size multiplier \"%s\"\n",
5597		   fragment_to_tempstr(&optionarg)));
5598	    {
5599		RegisDataFragment sizemultiplierarg;
5600		int sizemultiplier;
5601		int ww, hh;
5602
5603		if (!extract_regis_extent(&optionarg, &sizemultiplierarg)) {
5604		    TRACE(("DATA_ERROR: expected extent in size multiplier option: \"%s\"\n",
5605			   fragment_to_tempstr(&optionarg)));
5606		    break;
5607		}
5608		TRACE(("size multiplier: %s\n",
5609		       fragment_to_tempstr(&sizemultiplierarg)));
5610		/* FIXME: verify this is in pixels, not user coordinates */
5611		if (!load_regis_pixel_extent(fragment_to_tempstr(&sizemultiplierarg),
5612					     0, 0, &ww, &hh)) {
5613		    TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n",
5614			   state->option, fragment_to_tempstr(&sizemultiplierarg)));
5615		    break;
5616		}
5617		if (!regis_num_to_int(&sizemultiplierarg, &sizemultiplier)) {
5618		    TRACE(("DATA_ERROR: unable to parse extent in size multiplier option: \"%s\"\n",
5619			   fragment_to_tempstr(&sizemultiplierarg)));
5620		    break;
5621		}
5622		if (ww < 1 || hh < 1) {
5623		    TRACE(("DATA_ERROR: invalid size multiplier: %d,%d FIXME\n",
5624			   ww, hh));
5625		    break;
5626		}
5627		if (ww > 16) {
5628		    ww = 16;
5629		}
5630		if (hh > 16) {
5631		    hh = 16;
5632		}
5633
5634		TRACE(("using size multiplier: %d,%d\n", ww, hh));
5635
5636		/* times the S1 character unit cell dimensions */
5637		context->current_text_controls->character_unit_cell_w =
5638		    (unsigned) ww *8U;
5639		context->current_text_controls->character_unit_cell_h =
5640		    (unsigned) hh *20U;
5641
5642		if (fragment_len(&optionarg)) {
5643		    TRACE(("DATA_ERROR: ignoring trailing junk in text unit cell size option \"%s\"\n",
5644			   fragment_to_tempstr(&sizemultiplierarg)));
5645		    break;
5646		}
5647	    }
5648	    break;
5649	case 'S':
5650	case 's':
5651	    TRACE(("found display size or standard character cell size \"%s\"\n",
5652		   fragment_to_tempstr(&optionarg)));
5653	    for (;;) {
5654		RegisDataFragment displaysizearg;
5655
5656		if (extract_regis_extent(&optionarg, &displaysizearg)) {
5657		    int disp_w, disp_h;
5658
5659		    TRACE(("custom display size: %s\n",
5660			   fragment_to_tempstr(&displaysizearg)));
5661		    /* FIXME: verify this is in pixels, not user coordinates */
5662		    if (!load_regis_pixel_extent(fragment_to_tempstr(&displaysizearg),
5663						 0, 0, &disp_w, &disp_h)) {
5664			TRACE(("DATA_ERROR: unable to parse extent in text display size option: \"%s\"\n",
5665			       fragment_to_tempstr(&displaysizearg)));
5666			break;
5667		    }
5668		    if (disp_w < 1 || disp_h < 1) {
5669			TRACE(("DATA_ERROR: invalid text display size: %dx%d FIXME\n",
5670			       disp_w, disp_h));
5671			break;
5672		    }
5673
5674		    TRACE(("using display cell size: %d,%d\n", disp_w, disp_h));
5675		    context->current_text_controls->character_display_w =
5676			(unsigned) disp_w;
5677		    context->current_text_controls->character_display_h =
5678			(unsigned) disp_h;
5679		    TRACE(("using offset: %d,%d\n", disp_w, 0));
5680		    context->current_text_controls->character_inc_x = disp_w;
5681		    context->current_text_controls->character_inc_y = 0;
5682
5683		    continue;
5684		}
5685
5686		if (extract_regis_num(&optionarg, &displaysizearg)) {
5687		    int standard;
5688		    unsigned disp_w, disp_h, unit_w, unit_h;
5689		    int off_x, off_y;
5690
5691		    TRACE(("standard display cell size: %s\n",
5692			   fragment_to_tempstr(&displaysizearg)));
5693		    if (!regis_num_to_int(&displaysizearg, &standard)) {
5694			TRACE(("DATA_ERROR: unable to parse int in text standard cell size option: \"%s\"\n",
5695			       fragment_to_tempstr(&displaysizearg)));
5696			break;
5697		    }
5698		    if (get_standard_character_size(standard, &disp_w, &disp_h,
5699						    &unit_w, &unit_h,
5700						    &off_x, &off_y)) {
5701			TRACE(("DATA_ERROR: unrecognized standard cell size: \"%d\"\n",
5702			       standard));
5703			break;
5704		    }
5705
5706		    TRACE(("using display cell size: %u,%u\n", disp_w, disp_h));
5707		    context->current_text_controls->character_display_w = disp_w;
5708		    context->current_text_controls->character_display_h = disp_h;
5709		    TRACE(("using offset: %d,%d\n", off_x, off_y));
5710		    context->current_text_controls->character_inc_x = off_x;
5711		    context->current_text_controls->character_inc_y = off_y;
5712
5713		    /*
5714		     * Some ReGIS documentation implies that the "S" option only
5715		     * affects character spacing after a rotation option ("ReGIS
5716		     * uses the spacing value associated with the cell size to
5717		     * space the characters in the tilted string").  The 7-13
5718		     * example in the VT330/VT340 Programmer Reference Manual vol 2
5719		     * appears to say otherwise.  FIXME: verify
5720		     */
5721		    if (1 || !state->string_rot_set) {	/* forced for now */
5722			TRACE(("using unit cell size: %u,%u\n", unit_w, unit_h));
5723			context->current_text_controls->character_unit_cell_w =
5724			    unit_w;
5725			context->current_text_controls->character_unit_cell_h =
5726			    unit_h;
5727		    }
5728
5729		    continue;
5730		}
5731
5732		if (skip_regis_whitespace(&optionarg)) {
5733		    continue;
5734		}
5735
5736		if (!fragment_len(&optionarg)) {
5737		    break;
5738		}
5739
5740		TRACE(("DATA_ERROR: expected int or extent in text display size option: \"%s\"\n",
5741		       fragment_to_tempstr(&optionarg)));
5742		break;
5743	    }
5744	    break;
5745	case 'U':
5746	case 'u':
5747	    TRACE(("found text command custom unit cell size \"%s\"\n",
5748		   fragment_to_tempstr(&optionarg)));
5749	    {
5750		RegisDataFragment unitsizearg;
5751		int unitsize;
5752		int unit_w, unit_h;
5753
5754		if (!extract_regis_extent(&optionarg, &unitsizearg)) {
5755		    TRACE(("DATA_ERROR: expected extent in text unit cell size option: \"%s\"\n",
5756			   fragment_to_tempstr(&optionarg)));
5757		    break;
5758		}
5759		TRACE(("unitsize cell size: %s\n",
5760		       fragment_to_tempstr(&unitsizearg)));
5761		/* FIXME: verify this is in pixels, not user coordinates */
5762		if (!load_regis_pixel_extent(fragment_to_tempstr(&unitsizearg),
5763					     0, 0,
5764					     &unit_w, &unit_h)) {
5765		    TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n",
5766			   state->option, fragment_to_tempstr(&unitsizearg)));
5767		    break;
5768		}
5769		if (!regis_num_to_int(&unitsizearg, &unitsize)) {
5770		    TRACE(("DATA_ERROR: unable to parse extent in text unit cell size option: \"%s\"\n",
5771			   fragment_to_tempstr(&unitsizearg)));
5772		    break;
5773		}
5774		if (unit_w < 1 || unit_h < 1) {
5775		    TRACE(("DATA_ERROR: invalid text unit cell size: %dx%d FIXME\n",
5776			   unit_w, unit_h));
5777		    break;
5778		}
5779
5780		TRACE(("using unit cell size: %d,%d\n", unit_w, unit_h));
5781
5782		context->current_text_controls->character_unit_cell_w =
5783		    (unsigned) unit_w;
5784		context->current_text_controls->character_unit_cell_h =
5785		    (unsigned) unit_h;
5786
5787		if (fragment_len(&optionarg)) {
5788		    TRACE(("DATA_ERROR: ignoring trailing junk in text unit cell size option \"%s\"\n",
5789			   fragment_to_tempstr(&unitsizearg)));
5790		    break;
5791		}
5792	    }
5793	    break;
5794	case 'W':
5795	case 'w':
5796	    TRACE(("found temporary write options \"%s\"\n",
5797		   fragment_to_tempstr(&optionarg)));
5798	    if (!load_regis_write_control_set(state, context,
5799					      context->graphics_output_cursor_x,
5800					      context->graphics_output_cursor_y,
5801					      &optionarg,
5802					      &context->temporary_write_controls)) {
5803		TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n",
5804		       fragment_to_tempstr(&optionarg)));
5805		break;
5806	    }
5807	    break;
5808	default:
5809	    TRACE(("DATA_ERROR: ignoring unknown ReGIS text command option '%c' arg \"%s\"\n",
5810		   state->option, fragment_to_tempstr(&optionarg)));
5811	    break;
5812	}
5813	break;
5814    case 'v':
5815	TRACE(("inspecting vector option \"%c\" with value \"%s\"\n",
5816	       state->option, fragment_to_tempstr(&optionarg)));
5817	switch (state->option) {
5818	case 'B':
5819	case 'b':
5820	    TRACE(("found begin bounded position stack \"%s\"\n",
5821		   fragment_to_tempstr(&optionarg)));
5822	    skip_regis_whitespace(&optionarg);
5823	    if (fragment_len(&optionarg) > 0U) {
5824		TRACE(("DATA_ERROR: ignoring unexpected arguments to vector option '%c' arg \"%s\"\n",
5825		       state->option, fragment_to_tempstr(&optionarg)));
5826	    }
5827	    if (state->stack_next >= POSITION_STACK_SIZE) {
5828		/* FIXME: ignore, error, update counter? */
5829		TRACE(("unable to push position to full stack\n"));
5830		break;
5831	    }
5832
5833	    state->stack_x[state->stack_next] =
5834		context->graphics_output_cursor_x;
5835	    state->stack_y[state->stack_next] =
5836		context->graphics_output_cursor_y;
5837	    state->stack_next++;
5838	    break;
5839	case 'E':
5840	case 'e':
5841	    TRACE(("found end position stack \"%s\"\n",
5842		   fragment_to_tempstr(&optionarg)));
5843	    skip_regis_whitespace(&optionarg);
5844	    if (fragment_len(&optionarg) > 0U) {
5845		TRACE(("DATA_ERROR: ignoring unexpected arguments to vector option '%c' arg \"%s\"\n",
5846		       state->option, fragment_to_tempstr(&optionarg)));
5847	    }
5848	    if (state->stack_next == 0U) {
5849		TRACE(("DATA_ERROR: unable to pop position from empty stack\n"));
5850		break;
5851	    }
5852
5853	    state->stack_next--;
5854	    if (state->stack_x[state->stack_next] != DUMMY_STACK_X ||
5855		state->stack_y[state->stack_next] != DUMMY_STACK_Y) {
5856		int orig_x, orig_y;
5857
5858		orig_x = context->graphics_output_cursor_x;
5859		orig_y = context->graphics_output_cursor_y;
5860		context->graphics_output_cursor_x =
5861		    state->stack_x[state->stack_next];
5862		context->graphics_output_cursor_y =
5863		    state->stack_y[state->stack_next];
5864
5865		TRACE(("drawing line to popped location %d,%d\n",
5866		       context->graphics_output_cursor_x,
5867		       context->graphics_output_cursor_y));
5868		draw_patterned_line(context,
5869				    orig_x, orig_y,
5870				    context->graphics_output_cursor_x,
5871				    context->graphics_output_cursor_y);
5872	    }
5873	    break;
5874	case 'S':
5875	case 's':
5876	    TRACE(("found begin unbounded position stack \"%s\"\n",
5877		   fragment_to_tempstr(&optionarg)));
5878	    skip_regis_whitespace(&optionarg);
5879	    if (fragment_len(&optionarg) > 0U) {
5880		TRACE(("DATA_ERROR: ignoring unexpected arguments to vector option '%c' arg \"%s\"\n",
5881		       state->option, fragment_to_tempstr(&optionarg)));
5882	    }
5883	    if (state->stack_next >= POSITION_STACK_SIZE) {
5884		/* FIXME: ignore, error, update counter? */
5885		TRACE(("unable to push dummy position to full stack\n"));
5886		break;
5887	    }
5888
5889	    state->stack_x[state->stack_next] = DUMMY_STACK_X;
5890	    state->stack_y[state->stack_next] = DUMMY_STACK_Y;
5891	    state->stack_next++;
5892	    break;
5893	case 'W':
5894	case 'w':
5895	    TRACE(("found temporary write options \"%s\"\n",
5896		   fragment_to_tempstr(&optionarg)));
5897	    if (!load_regis_write_control_set(state, context,
5898					      context->graphics_output_cursor_x,
5899					      context->graphics_output_cursor_y,
5900					      &optionarg,
5901					      &context->temporary_write_controls)) {
5902		TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n",
5903		       fragment_to_tempstr(&optionarg)));
5904	    }
5905	    break;
5906	default:
5907	    TRACE(("DATA_ERROR: ignoring unknown ReGIS vector command option '%c' arg \"%s\"\n",
5908		   state->option, fragment_to_tempstr(&optionarg)));
5909	    break;
5910	}
5911	break;
5912    case 'w':
5913	skip_regis_whitespace(&optionarg);
5914	TRACE(("inspecting permanent write option \"%c\" with value \"%s\"\n",
5915	       state->option, fragment_to_tempstr(&optionarg)));
5916	if (!load_regis_write_control(state, context,
5917				      context->graphics_output_cursor_x,
5918				      context->graphics_output_cursor_y,
5919				      state->option, &optionarg,
5920				      &context->persistent_write_controls)) {
5921	    TRACE(("DATA_ERROR: invalid write options\n"));
5922	    return 1;
5923	}
5924	break;
5925    default:
5926	TRACE(("DATA_ERROR: unexpected option in \"%c\" command: \"%s\"\n",
5927	       state->command, fragment_to_tempstr(&optionarg)));
5928	return 1;
5929    }
5930
5931    return 1;
5932}
5933
5934static int
5935parse_regis_items(RegisParseState *state, RegisGraphicsContext *context)
5936{
5937    RegisDataFragment *const input = &state->input;
5938    RegisDataFragment item;
5939
5940    if (input->pos >= input->len)
5941	return 0;
5942
5943    if (extract_regis_extent(input, &item)) {
5944	TRACE(("found extent \"%s\"\n", fragment_to_tempstr(&item)));
5945	switch (state->command) {
5946	case 'c':
5947	    {
5948		int orig_x, orig_y;
5949		int new_x, new_y;
5950
5951		if (state->num_points > 0) {
5952		    orig_x = state->x_points[state->num_points - 1];
5953		    orig_y = state->y_points[state->num_points - 1];
5954		} else {
5955		    orig_x = context->graphics_output_cursor_x;
5956		    orig_y = context->graphics_output_cursor_y;
5957		}
5958		if (!load_regis_coord_extent(context,
5959					     fragment_to_tempstr(&item),
5960					     orig_x, orig_y,
5961					     &new_x, &new_y)) {
5962		    TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n",
5963			   state->command, fragment_to_tempstr(&item)));
5964		    break;
5965		}
5966
5967		switch (state->curve_mode) {
5968		case CURVE_POSITION_ARC_CENTER:
5969		case CURVE_POSITION_ARC_EDGE:
5970		    {
5971			double radians;
5972			int degrees;
5973			int c_x, c_y;
5974			int e_x, e_y;
5975			int e_x_final = 0, e_y_final = 0;
5976
5977			if (state->curve_mode == CURVE_POSITION_ARC_CENTER) {
5978			    c_x = new_x;
5979			    c_y = new_y;
5980			    e_x = orig_x;
5981			    e_y = orig_y;
5982			} else {
5983			    c_x = orig_x;
5984			    c_y = orig_y;
5985			    e_x = new_x;
5986			    e_y = new_y;
5987			}
5988
5989			radians = atan2((double) (c_y - e_y),
5990					(double) (e_x - c_x));
5991			degrees = (int) (360.0 * radians / (2.0 * M_PI));
5992			if (degrees < 0)
5993			    degrees += 360;
5994
5995			TRACE(("drawing arc centered at location %d,%d to location %d,%d from %d degrees (%g radians) for %d degrees\n",
5996			       c_x, c_y,
5997			       e_x, e_y,
5998			       degrees, radians, state->arclen));
5999			draw_patterned_arc(context,
6000					   c_x, c_y,
6001					   e_x, e_y,
6002					   degrees, state->arclen,
6003					   &e_x_final, &e_y_final);
6004
6005#ifdef DEBUG_ARC_CENTER
6006			DRAW_PIXEL(context, c_x + 1, c_y, 3U);
6007			DRAW_PIXEL(context, c_x - 1, c_y, 3U);
6008			DRAW_PIXEL(context, c_x, c_y + 1, 3U);
6009			DRAW_PIXEL(context, c_x, c_y - 1, 3U);
6010			DRAW_PIXEL(context, c_x, c_y, 3U);
6011#endif
6012
6013#ifdef DEBUG_ARC_START
6014			DRAW_PIXEL(context, e_x + 1, e_y, 2U);
6015			DRAW_PIXEL(context, e_x - 1, e_y, 2U);
6016			DRAW_PIXEL(context, e_x, e_y + 1, 2U);
6017			DRAW_PIXEL(context, e_x, e_y - 1, 2U);
6018			DRAW_PIXEL(context, e_x, e_y, 2U);
6019#endif
6020
6021#ifdef DEBUG_ARC_END
6022			DRAW_PIXEL(context, e_x_final + 1, e_y_final + 1, 1U);
6023			DRAW_PIXEL(context, e_x_final + 1, e_y_final - 1, 1U);
6024			DRAW_PIXEL(context, e_x_final - 1, e_y_final + 1, 1U);
6025			DRAW_PIXEL(context, e_x_final - 1, e_y_final - 1, 1U);
6026			DRAW_PIXEL(context, e_x_final, e_y_final, 1U);
6027#endif
6028
6029			if (state->curve_mode == CURVE_POSITION_ARC_CENTER) {
6030			    TRACE(("moving cursor to final point on arc %d,%d\n",
6031				   e_x_final, e_y_final));
6032			    if (state->num_points > 0) {
6033				state->x_points[state->num_points - 1] =
6034				    e_x_final;
6035				state->y_points[state->num_points - 1] =
6036				    e_y_final;
6037			    }
6038			    context->graphics_output_cursor_x = e_x_final;
6039			    context->graphics_output_cursor_y = e_y_final;
6040			}
6041		    }
6042		    break;
6043		case CURVE_POSITION_OPEN_CURVE:
6044		case CURVE_POSITION_CLOSED_CURVE:
6045		    if (state->num_points >= MAX_INPUT_CURVE_POINTS) {
6046			TRACE(("DATA_ERROR: got curve point, but already have max points (%d)\n",
6047			       state->num_points));
6048			break;
6049		    }
6050		    state->x_points[state->num_points] = new_x;
6051		    state->y_points[state->num_points] = new_y;
6052		    state->num_points++;
6053		    TRACE(("adding point to curve with location %d,%d\n",
6054			   new_x, new_y));
6055		    break;
6056		default:
6057		    TRACE(("ERROR: got position, but curve mode %d is unknown\n",
6058			   state->curve_mode));
6059		    break;
6060		}
6061	    }
6062	    break;
6063	case 'p':
6064	    /* FIXME TRACE(("DATA_ERROR: ignoring pen command with no location\n")); */
6065	    if (!load_regis_coord_extent(context,
6066					 fragment_to_tempstr(&item),
6067					 context->graphics_output_cursor_x,
6068					 context->graphics_output_cursor_y,
6069					 &context->graphics_output_cursor_x,
6070					 &context->graphics_output_cursor_y)) {
6071		TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n",
6072		       state->command, fragment_to_tempstr(&item)));
6073		break;
6074	    }
6075	    TRACE(("moving pen to location %d,%d\n",
6076		   context->graphics_output_cursor_x,
6077		   context->graphics_output_cursor_y));
6078	    break;
6079	case 's':
6080	    TRACE(("extent in screen command\n"));
6081	    {
6082		int new_x, new_y;
6083		int copy_w, copy_h;
6084
6085		if (!load_regis_coord_extent(context,
6086					     fragment_to_tempstr(&item),
6087					     0, 0, &new_x, &new_y)) {
6088		    TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n",
6089			   state->command, fragment_to_tempstr(&item)));
6090		    break;
6091		}
6092		TRACE(("scrolling image to coordinates %d,%d\n", new_x, new_y));
6093		new_x -= context->x_off;
6094		new_y -= context->y_off;
6095		TRACE(("scrolling image to buffer location %d,%d\n", new_x, new_y));
6096		/* FIXME: does any write mode affect background? */
6097		if (new_y < 0)
6098		    copy_h = context->height - new_y;
6099		else
6100		    copy_h = context->height;
6101		if (new_x < 0)
6102		    copy_w = context->width - new_x;
6103		else
6104		    copy_w = context->width;
6105		copy_overlapping_area(context->graphic, 0, 0, new_x, new_y,
6106				      (unsigned) copy_w, (unsigned) copy_h,
6107				      context->background);
6108	    }
6109	    break;
6110	case 't':
6111	    /* FIXME: verify this is in pixels, not user coordinates */
6112	    if (!load_regis_pixel_extent(fragment_to_tempstr(&item),
6113					 0, 0,
6114					 &context->current_text_controls->character_inc_x,
6115					 &context->current_text_controls->character_inc_y)) {
6116		TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n",
6117		       state->command, fragment_to_tempstr(&item)));
6118		break;
6119	    }
6120	    TRACE(("setting character spacing to %d,%d\n",
6121		   context->current_text_controls->character_inc_x,
6122		   context->current_text_controls->character_inc_y));
6123	    break;
6124	case 'v':
6125	    {
6126		int orig_x, orig_y;
6127
6128		orig_x = context->graphics_output_cursor_x;
6129		orig_y = context->graphics_output_cursor_y;
6130		if (!load_regis_coord_extent(context,
6131					     fragment_to_tempstr(&item),
6132					     orig_x, orig_y,
6133					     &context->graphics_output_cursor_x,
6134					     &context->graphics_output_cursor_y)) {
6135		    TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n",
6136			   state->command, fragment_to_tempstr(&item)));
6137		    break;
6138		}
6139		TRACE(("drawing line to location %d,%d\n",
6140		       context->graphics_output_cursor_x,
6141		       context->graphics_output_cursor_y));
6142		draw_patterned_line(context,
6143				    orig_x, orig_y,
6144				    context->graphics_output_cursor_x,
6145				    context->graphics_output_cursor_y);
6146	    }
6147	    break;
6148	default:
6149	    TRACE(("DATA_ERROR: unexpected extent in \"%c\" command: \"%s\"\n",
6150		   state->command, fragment_to_tempstr(&item)));
6151	    break;
6152	}
6153	return 1;
6154    }
6155
6156    if (state->command != 'l' && extract_regis_pixelvector(input, &item)) {
6157	TRACE(("found pixel vector \"%s\"\n", fragment_to_tempstr(&item)));
6158	switch (state->command) {
6159	case 'c':
6160	    /* FIXME: parse, handle */
6161	    TRACE(("pixelvector in curve command FIXME\n"));
6162	    break;
6163	    /* FIXME: not sure if 'f' supports pvs */
6164	case 'p':
6165	    /* FIXME: error checking */
6166	    if (!load_regis_pixelvector(fragment_to_tempstr(&item),
6167					context->temporary_write_controls.pv_multiplier,
6168					context->graphics_output_cursor_x,
6169					context->graphics_output_cursor_y,
6170					&context->graphics_output_cursor_x,
6171					&context->graphics_output_cursor_y)) {
6172		TRACE(("DATA_ERROR: unable to parse pixel vector in '%c' command: \"%s\"\n",
6173		       state->command, fragment_to_tempstr(&item)));
6174		break;
6175	    }
6176	    TRACE(("moving pen to location %d,%d\n",
6177		   context->graphics_output_cursor_x,
6178		   context->graphics_output_cursor_y));
6179	    break;
6180	case 's':
6181	    TRACE(("pixelvector in screen command\n"));
6182	    {
6183		int new_x, new_y;
6184		int copy_w, copy_h;
6185
6186		if (!load_regis_pixelvector(fragment_to_tempstr(&item),
6187					    context->temporary_write_controls.pv_multiplier,
6188					    0, 0, &new_x, &new_y)) {
6189		    TRACE(("DATA_ERROR: unable to parse pixel vector in '%c' command: \"%s\"\n",
6190			   state->command, fragment_to_tempstr(&item)));
6191		    break;
6192		}
6193		TRACE(("scrolling image to coordinates %d,%d\n", new_x, new_y));
6194		new_x -= context->x_off;
6195		new_y -= context->y_off;
6196		TRACE(("scrolling image to buffer location %d,%d\n", new_x, new_y));
6197		/* FIXME: does any write mode affect background? */
6198		if (new_y < 0)
6199		    copy_h = context->height - new_y;
6200		else
6201		    copy_h = context->height;
6202		if (new_x < 0)
6203		    copy_w = context->width - new_x;
6204		else
6205		    copy_w = context->width;
6206		copy_overlapping_area(context->graphic, 0, 0, new_x, new_y,
6207				      (unsigned) copy_w, (unsigned) copy_h,
6208				      context->background);
6209	    }
6210	    break;
6211	case 't':
6212	    {
6213		int dx, dy;
6214
6215		if (!load_regis_pixelvector(fragment_to_tempstr(&item), 1,
6216					    0, 0, &dx, &dy)) {
6217		    TRACE(("DATA_ERROR: unable to parse pixel vector in '%c' command: \"%s\"\n",
6218			   state->command, fragment_to_tempstr(&item)));
6219		    break;
6220		}
6221
6222		dx *= (int) (
6223				context->current_text_controls->character_display_w
6224				>> 1U);
6225		dy *= (int) (
6226				context->current_text_controls->character_display_h
6227				>> 1U);
6228		TRACE(("adding character offset %d,%d\n", dx, dy));
6229		context->graphics_output_cursor_x += dx;
6230		context->graphics_output_cursor_y += dy;
6231	    }
6232	    break;
6233	case 'v':
6234	    /* FIXME: error checking */
6235	    {
6236		int orig_x, orig_y;
6237
6238		orig_x = context->graphics_output_cursor_x;
6239		orig_y = context->graphics_output_cursor_y;
6240		if (!load_regis_pixelvector(fragment_to_tempstr(&item),
6241					    context->temporary_write_controls.pv_multiplier,
6242					    orig_x, orig_y,
6243					    &context->graphics_output_cursor_x,
6244					    &context->graphics_output_cursor_y)) {
6245		    TRACE(("DATA_ERROR: unable to parse pixel vector in '%c' command: \"%s\"\n",
6246			   state->command, fragment_to_tempstr(&item)));
6247		    break;
6248		}
6249		TRACE(("drawing line to location %d,%d\n",
6250		       context->graphics_output_cursor_x,
6251		       context->graphics_output_cursor_y));
6252		draw_patterned_line(context, orig_x, orig_y,
6253				    context->graphics_output_cursor_x,
6254				    context->graphics_output_cursor_y);
6255	    }
6256	    break;
6257	default:
6258	    TRACE(("DATA_ERROR: unexpected pixel vector in \"%c\" command: \"%s\"\n",
6259		   state->command, fragment_to_tempstr(&item)));
6260	    break;
6261	}
6262	return 1;
6263    }
6264
6265    if (extract_regis_string(input, state->temp, state->templen)) {
6266	switch (state->command) {
6267	case 'l':
6268	    /* FIXME: confirm that extra characters are ignored */
6269	    TRACE(("found character to load: \"%s\"\n", state->temp));
6270	    state->load_glyph = (unsigned) (unsigned char) state->temp[0];
6271	    state->load_row = 0U;
6272	    break;
6273	case 't':
6274	    TRACE(("found string to draw: \"%s\"\n", state->temp));
6275	    draw_text(context, state->temp);
6276	    break;
6277	default:
6278	    TRACE(("DATA_ERROR: unexpected string in \"%c\" command: \"%s\"\n",
6279		   state->command, state->temp));
6280	    break;
6281	}
6282	return 1;
6283    }
6284
6285    /* hex values */
6286    if (state->command == 'l') {
6287	char ch;
6288	unsigned digit;
6289
6290	for (digit = 0U; digit < (state->load_w + 3U) >> 2U; digit++) {
6291	    ch = peek_fragment(input);
6292
6293	    if (!IS_HEX_DIGIT(ch)) {
6294		if (ch != ',' && ch != ';' &&
6295		    ch != ' ' && ch != '\r' &&
6296		    ch != '\n') {
6297		    TRACE(("found end of hexadecimal string witch '%c' on digit %u\n",
6298			   ch, digit));
6299		    /* FIXME: need to unput the digits up to this point */
6300		    /*
6301		     * Report success since we ate some characters,
6302		     * and the new char needs to be compared with commands
6303		     * and other top-level things.
6304		     */
6305		    if (digit != 0U)
6306			return 1;
6307		    return 0;
6308		}
6309		pop_fragment(input);
6310		break;
6311	    }
6312
6313	    state->temp[digit] = ch;
6314	    pop_fragment(input);
6315	}
6316	state->temp[digit] = '\0';
6317
6318	if (strlen(state->temp) > 0) {
6319	    unsigned long val;
6320	    unsigned glyph_size;
6321
6322	    val = strtoul(state->temp, NULL, 16);
6323	    TRACE(("found row %u for glyph %u: \"%s\" value %02lx (%lu)\n",
6324		   state->load_row, state->load_glyph, state->temp, val, val));
6325
6326	    if (state->load_row >= state->load_h) {
6327		TRACE(("DATA_ERROR: ignoring extra row for glyph %u\n",
6328		       state->load_glyph));
6329		return 0;
6330	    }
6331
6332	    if (state->load_index == MAX_REGIS_ALPHABETS) {
6333		state->load_index = find_free_alphabet_index(context,
6334							     state->load_alphabet,
6335							     state->load_w,
6336							     state->load_h);
6337		TRACE(("current alphabet is %u and size is %ux%u; assigning alphabet index %u\n",
6338		       state->load_alphabet, state->load_w, state->load_h, state->load_index));
6339	    }
6340
6341	    glyph_size = GLYPH_WIDTH_BYTES(
6342					      context->alphabets[state->load_index].pixw) *
6343		context->alphabets[state->load_index].pixh;
6344	    if (context->alphabets[state->load_index].bytes == NULL) {
6345		if (!(context->alphabets[state->load_index].bytes =
6346		      calloc(MAX_GLYPHS * glyph_size, sizeof(unsigned char)))) {
6347		    TRACE(("ERROR: unable to allocate %u bytes for glyph storage\n",
6348			   MAX_GLYPHS * glyph_size));
6349		    return 0;
6350		}
6351	    } {
6352		unsigned char *glyph;
6353		unsigned bytew;
6354		unsigned byte;
6355		unsigned unused_bits;
6356
6357		glyph = &context->alphabets[state->load_index]
6358		    .bytes[state->load_glyph * glyph_size];
6359		bytew = GLYPH_WIDTH_BYTES(context->alphabets[state->load_index]
6360					  .pixw);
6361		unused_bits = 8U - (context->alphabets[state->load_index].pixw
6362				    & 3U);
6363		if (unused_bits == 8U) {
6364		    unused_bits = 0U;
6365		}
6366		for (byte = 0U; byte < bytew; byte++) {
6367		    glyph[state->load_row * bytew + byte] =
6368			(unsigned char) (((val << unused_bits) >>
6369					  ((bytew - (byte + 1U)) << 3U)) & 255U);
6370#ifdef DEBUG_LOAD
6371		    TRACE(("bytew=%u val=%lx byte=%u output=%x\n", bytew, val,
6372			   byte,
6373			   (unsigned) glyph[state->load_row * bytew + byte]));
6374#endif
6375		}
6376
6377		state->load_row++;
6378		context->alphabets[state->load_index]
6379		    .loaded[state->load_glyph] = 1;
6380#ifdef DEBUG_LOAD
6381		TRACE(("marking alphabet %u at index %u glyph %u as loaded\n",
6382		       state->load_alphabet, state->load_index,
6383		       state->load_glyph));
6384#endif
6385		return 1;
6386	    }
6387	}
6388    }
6389
6390    return 0;
6391}
6392
6393static int
6394parse_regis_toplevel(RegisParseState *state, RegisGraphicsContext *context)
6395{
6396    RegisDataFragment parenthesized;
6397    char ch;
6398
6399    TRACE(("parsing top level: char %d of %d (next char '%c')\n",
6400	   state->input.pos,
6401	   state->input.len,
6402	   peek_fragment(&state->input)));
6403    if (skip_regis_whitespace(&state->input))
6404	return 0;
6405    /* FIXME: the semicolon terminates the current command even if inside of an optionset or extent */
6406    if (peek_fragment(&state->input) == ';') {
6407	pop_fragment(&state->input);
6408	TRACE(("ending '%c' command\n", state->command));
6409	state->command = '_';
6410	state->option = '_';
6411	return 1;
6412    }
6413    /* Load statements contain hex values which may look like commands. */
6414    ch = peek_fragment(&state->input);
6415    if (state->command != 'l' || !IS_HEX_DIGIT(ch)) {
6416	TRACE(("checking for top level command...\n"));
6417	if (parse_regis_command(state)) {
6418	    context->graphic->dirty = 1;
6419
6420	    /* FIXME: verify that these are the things reset on a new command */
6421	    TRACE(("resetting temporary write controls and pattern state\n"));
6422	    copy_regis_write_controls(&context->persistent_write_controls,
6423				      &context->temporary_write_controls);
6424	    context->pattern_count = 0U;
6425	    context->pattern_bit = 1U;
6426
6427	    /* FIXME: what happens if temporary text controls aren't closed? */
6428	    /* FIXME: what if temporary text controls are nested? */
6429	    context->current_text_controls = &context->persistent_text_controls;
6430	    return 1;
6431	}
6432    }
6433    TRACE(("checking for top level parentheses...\n"));
6434    if (extract_regis_parenthesized_data(&state->input, &parenthesized)) {
6435	RegisDataFragment orig_input;
6436
6437	if (state->command == 'f') {	/* Fill */
6438	    TRACE(("found commands in fill mode \"%s\"\n",
6439		   fragment_to_tempstr(&parenthesized)));
6440	    orig_input = state->input;
6441	    state->input = parenthesized;
6442	    state->command = '_';
6443	    state->option = '_';
6444	    context->fill_mode = 1;
6445	    context->fill_point_count = 0U;
6446	    while (state->input.pos < state->input.len)
6447		parse_regis_toplevel(state, context);
6448	    draw_filled_polygon(context);
6449	    context->fill_point_count = 0U;
6450	    context->fill_mode = 0;
6451	    state->command = 'f';
6452	    state->input = orig_input;
6453	    return 1;
6454	} else {
6455	    orig_input = state->input;
6456	    state->input = parenthesized;
6457	    state->option = '_';
6458	    TRACE(("parsing at optionset level: %d of %d\n",
6459		   state->input.pos,
6460		   state->input.len));
6461	    for (;;) {
6462		if (state->input.pos >= state->input.len)
6463		    break;
6464		TRACE(("looking at optionset character: \"%c\"\n",
6465		       peek_fragment(&state->input)));
6466		if (skip_regis_whitespace(&state->input))
6467		    continue;
6468		if (parse_regis_option(state, context))
6469		    continue;
6470		if (parse_regis_items(state, context))
6471		    continue;
6472		if (state->input.pos >= state->input.len)
6473		    break;
6474		{
6475		    char skip;
6476
6477		    skip = pop_fragment(&state->input);
6478		    (void) skip;	/* variable needed only if tracing */
6479		    TRACE(("DATA_ERROR: skipping unexpected character in optionset: \"%c\"\n",
6480			   skip));
6481		}
6482		/* FIXME: suboptions */
6483	    }
6484	    state->option = '_';
6485	    state->input = orig_input;
6486	    return 1;
6487	}
6488    }
6489    if (state->command == 'f') {	/* Fill */
6490	RegisDataFragment optionarg;
6491	if (extract_regis_option(&state->input, &state->option, &optionarg)) {
6492	    skip_regis_whitespace(&optionarg);
6493
6494	    TRACE(("found temporary write options \"%s\"\n",
6495		   fragment_to_tempstr(&optionarg)));
6496	    if (!load_regis_write_control_set(state, context,
6497					      context->graphics_output_cursor_x,
6498					      context->graphics_output_cursor_y,
6499					      &optionarg,
6500					      &context->temporary_write_controls)) {
6501		TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n",
6502		       fragment_to_tempstr(&optionarg)));
6503	    }
6504	    return 1;
6505	}
6506	TRACE(("checking for top level items (though none should be present)...\n"));
6507	if (parse_regis_items(state, context))
6508	    return 1;
6509    } else {
6510	TRACE(("checking for top level items...\n"));
6511	if (parse_regis_items(state, context))
6512	    return 1;
6513    }
6514    if (state->input.pos >= state->input.len)
6515	return 0;
6516
6517    {
6518	char skip;
6519
6520	skip = pop_fragment(&state->input);
6521	(void) skip;		/* variable needed only if tracing */
6522	TRACE(("DATA_ERROR: skipping unexpected character at top level: \"%c\"\n", ch));
6523    }
6524    return 0;
6525}
6526
6527void
6528parse_regis(XtermWidget xw, ANSI *params, char const *string)
6529{
6530    TScreen *screen = TScreenOf(xw);
6531    RegisGraphicsContext *const context = &persistent_context;
6532    RegisParseState state;
6533    struct timeval prev_tv;
6534    struct timeval curr_tv;
6535    unsigned iterations;
6536    int charrow = 0;
6537    int charcol = 0;
6538    unsigned type = 1;		/* FIXME: use page number */
6539    int Pmode;
6540
6541    if (params->a_nparam > 0)
6542	Pmode = params->a_param[0];
6543    else
6544	Pmode = 0;
6545
6546    TRACE(("ReGIS vector graphics mode, param_count=%d mode=%d\n",
6547	   params->a_nparam, Pmode));
6548
6549    init_fragment(&state.input, string);
6550    state.templen = (unsigned) strlen(string) + 1U;
6551    if (!(state.temp = malloc((size_t) state.templen))) {
6552	TRACE(("Unable to allocate temporary buffer of size %u\n",
6553	       state.templen));
6554	return;
6555    }
6556    state.command = '_';
6557    state.option = '_';
6558    state.stack_next = 0U;
6559    state.load_index = MAX_REGIS_ALPHABETS;
6560
6561    /* Update the screen scrolling and do a refresh.
6562     * The refresh may not cover the whole graphic.
6563     */
6564    if (screen->scroll_amt)
6565	FlushScroll(xw);
6566
6567    /* Only reset on the first ReGIS image unless it is being requested. */
6568    if (context->width == 0 || context->height == 0 ||
6569	Pmode == 1 || Pmode == 3) {
6570	init_regis_graphics_context(screen->terminal_id,
6571				    screen->graphics_regis_def_wide,
6572				    screen->graphics_regis_def_high,
6573				    get_color_register_count(screen),
6574				    screen->graphics_regis_default_font,
6575				    context);
6576    }
6577
6578    context->graphic = get_new_or_matching_graphic(xw,
6579						   charrow, charcol,
6580						   context->width,
6581						   context->height,
6582						   type);
6583    context->graphic->valid = 1;
6584    context->graphic->dirty = 1;
6585
6586    X_GETTIMEOFDAY(&prev_tv);
6587    iterations = 0U;
6588    refresh_modified_displayed_graphics(xw);
6589
6590    for (;;) {
6591	if (skip_regis_whitespace(&state.input))
6592	    continue;
6593	iterations++;
6594	if (parse_regis_toplevel(&state, context)) {
6595	    if (iterations > MIN_ITERATIONS_BEFORE_REFRESH) {
6596		int need_refresh = 0;
6597
6598		X_GETTIMEOFDAY(&curr_tv);
6599		if (curr_tv.tv_sec > prev_tv.tv_sec + 1U) {
6600		    need_refresh = 1;
6601		} else {
6602#define DiffTime(tv) (tv.tv_sec * 1000L + tv.tv_usec / 1000L)
6603		    long diff = (long) (DiffTime(curr_tv) - DiffTime(prev_tv));
6604		    if (diff > MIN_MS_BEFORE_REFRESH) {
6605			need_refresh = 1;
6606		    }
6607		}
6608		if (need_refresh) {
6609		    /* FIXME: pre-ANSI compilers need memcpy() */
6610		    prev_tv = curr_tv;
6611		    iterations = 0U;
6612		    refresh_modified_displayed_graphics(xw);
6613#if OPT_DOUBLE_BUFFER
6614		    {
6615			XdbeSwapInfo swap;
6616
6617			swap.swap_window = VWindow(screen);
6618			swap.swap_action = XdbeCopied;
6619			XdbeSwapBuffers(XtDisplay(term), &swap, 1);
6620			XFlush(XtDisplay(xw));
6621		    }
6622#endif
6623		}
6624	    }
6625	    continue;
6626	}
6627
6628	if (state.input.pos >= state.input.len)
6629	    break;
6630    }
6631
6632    free(state.temp);
6633
6634    refresh_modified_displayed_graphics(xw);
6635    TRACE(("DONE! Successfully parsed ReGIS data.\n"));
6636}
6637