1/*
2
3Copyright 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25*/
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30#include <X11/Xfuncs.h>
31#include "Xct.h"
32#include <stdio.h>
33#include "Xmuint.h"
34
35#define UsedGraphic	0x0001
36#define UsedDirection	0x0002
37
38typedef struct _XctPriv {
39    XctString		ptr;
40    XctString		ptrend;
41    unsigned		flags;
42    XctHDirection	*dirstack;
43    unsigned		dirsize;
44    char		**encodings;
45    unsigned		enc_count;
46    XctString		itembuf;
47    unsigned		buf_count;
48} *XctPriv;
49
50#define IsMore(priv) ((priv)->ptr != (priv)->ptrend)
51#define AmountLeft(priv) ((priv)->ptrend - (priv)->ptr)
52
53#include <stdlib.h>
54
55#define HT	0x09
56#define NL	0x0a
57#define ESC	0x1b
58#define CSI	0x9b
59
60#define IsLegalC0(data, c) (((c) == HT) || ((c) == NL) || \
61			    (((data)->version > XctVersion) && \
62			     ((data)->flags & XctAcceptC0Extensions)))
63
64#define IsLegalC1(priv, c) (((data)->version > XctVersion) && \
65			    ((data)->flags & XctAcceptC1Extensions))
66
67#define IsI2(c) (((c) >= 0x20) && ((c) <= 0x2f))
68#define IsI3(c) (((c) >= 0x30) && ((c) <= 0x3f))
69#define IsESCF(c) (((c) >= 0x30) && ((c) <= 0x7e))
70#define IsCSIF(c) (((c) >= 0x40) && ((c) <= 0x7e))
71#define IsC0(c) ((c) <= 0x1f)
72#define IsGL(c) (((c) >= 0x20) && ((c) <= 0x7f))
73#define IsC1(c) (((c) >= 0x80) && ((c) <= 0x9f))
74#define IsGR(c) ((c) >= 0xa0)
75
76#define HasC  1
77#define HasGL 2
78#define HasGR 4
79#define ToGL  8
80
81/*
82 * Prototypes
83 */
84static void ComputeGLGR(XctData);
85static int Handle94GR(XctData, int);
86static int Handle96GR(XctData, int);
87static int HandleExtended(XctData data, int);
88static int HandleGL(XctData, int);
89static int HandleMultiGL(XctData, int);
90static int HandleMultiGR(XctData data, int);
91static void ShiftGRToGL(XctData, int);
92
93/*
94 * Implementation
95 */
96static void
97ComputeGLGR(register XctData data)
98{
99    /* XXX this will need more work if more sets are registered */
100    if ((data->GL_set_size == 94) && (data->GL_char_size == 1) &&
101	(data->GL[0] == '\102') &&
102	(data->GR_set_size == 96) && (data->GR_char_size == 1))
103	data->GLGR_encoding = data->GR_encoding;
104    else if ((data->GL_set_size == 94) && (data->GL_char_size == 1) &&
105	     (data->GL[0] == '\112') &&
106	     (data->GR_set_size == 94) && (data->GR_char_size == 1))
107	data->GLGR_encoding = data->GR_encoding;
108    else
109	data->GLGR_encoding = (char *)NULL;
110}
111
112static int
113HandleGL(register XctData data, int c)
114{
115    switch (c) {
116    case 0x42:
117	data->GL = "\102";
118	data->GL_encoding = "ISO8859-1";
119	break;
120    case 0x4a:
121	data->GL = "\112";
122	data->GL_encoding = "JISX0201.1976-0";
123	break;
124    default:
125	return 0;
126    }
127    data->GL_set_size = 94;
128    data->GL_char_size = 1;
129    ComputeGLGR(data);
130    return 1;
131}
132
133static int
134HandleMultiGL(register XctData data, int c)
135{
136    switch (c) {
137    case 0x41:
138	data->GL = "\101";
139	data->GL_encoding = "GB2312.1980-0";
140	break;
141    case 0x42:
142	data->GL = "\102";
143	data->GL_encoding = "JISX0208.1983-0";
144	break;
145    case 0x43:
146	data->GL = "\103";
147	data->GL_encoding = "KSC5601.1987-0";
148	break;
149    default:
150	return 0;
151    }
152    data->GL_set_size = 94;
153    data->GL_char_size = 2;
154#ifdef notdef
155    if (c < 0x60)
156	data->GL_char_size = 2;
157    else if (c < 0x70)
158	data->GL_char_size = 3;
159    else
160	data->GL_char_size = 4;
161#endif
162    data->GLGR_encoding = (char *)NULL;
163    return 1;
164}
165
166static int
167Handle94GR(register XctData data, int c)
168{
169    switch (c) {
170    case 0x49:
171	data->GR = "\111";
172	data->GR_encoding = "JISX0201.1976-0";
173	break;
174    default:
175	return 0;
176    }
177    data->priv->flags &= ~ToGL;
178    data->GR_set_size = 94;
179    data->GR_char_size = 1;
180    data->GLGR_encoding = (char *)NULL;
181    return 1;
182}
183
184static int
185Handle96GR(register XctData data, int c)
186{
187    switch (c) {
188    case 0x41:
189	data->GR = "\101";
190	data->GR_encoding = "ISO8859-1";
191	break;
192    case 0x42:
193	data->GR = "\102";
194	data->GR_encoding = "ISO8859-2";
195	break;
196    case 0x43:
197	data->GR = "\103";
198	data->GR_encoding = "ISO8859-3";
199	break;
200    case 0x44:
201	data->GR = "\104";
202	data->GR_encoding = "ISO8859-4";
203	break;
204    case 0x46:
205	data->GR = "\106";
206	data->GR_encoding = "ISO8859-7";
207	break;
208    case 0x47:
209	data->GR = "\107";
210	data->GR_encoding = "ISO8859-6";
211	break;
212    case 0x48:
213	data->GR = "\110";
214	data->GR_encoding = "ISO8859-8";
215	break;
216    case 0x4c:
217	data->GR = "\114";
218	data->GR_encoding = "ISO8859-5";
219	break;
220    case 0x4d:
221	data->GR = "\115";
222	data->GR_encoding = "ISO8859-9";
223	break;
224    default:
225	return 0;
226    }
227    data->priv->flags &= ~ToGL;
228    data->GR_set_size = 96;
229    data->GR_char_size = 1;
230    ComputeGLGR(data);
231    return 1;
232}
233
234static int
235HandleMultiGR(register XctData data, int c)
236{
237    switch (c) {
238    case 0x41:
239	data->GR = "\101";
240	if (data->flags & XctShiftMultiGRToGL)
241	    data->GR_encoding = "GB2312.1980-0";
242	else
243	    data->GR_encoding = "GB2312.1980-1";
244	break;
245    case 0x42:
246	data->GR = "\102";
247	if (data->flags & XctShiftMultiGRToGL)
248	    data->GR_encoding = "JISX0208.1983-0";
249	else
250	    data->GR_encoding = "JISX0208.1983-1";
251	break;
252    case 0x43:
253	data->GR = "\103";
254	if (data->flags & XctShiftMultiGRToGL)
255	    data->GR_encoding = "KSC5601.1987-0";
256	else
257	    data->GR_encoding = "KSC5601.1987-1";
258	break;
259    default:
260	return 0;
261    }
262    if (data->flags & XctShiftMultiGRToGL)
263	data->priv->flags |= ToGL;
264    else
265	data->priv->flags &= ~ToGL;
266    data->GR_set_size = 94;
267    data->GR_char_size = 2;
268#ifdef notdef
269    if (c < 0x60)
270	data->GR_char_size = 2;
271    else if (c < 0x70)
272	data->GR_char_size = 3;
273    else
274	data->GR_char_size = 4;
275#endif
276    data->GLGR_encoding = (char *)NULL;
277    return 1;
278}
279
280static int
281HandleExtended(register XctData data, int c)
282{
283    register XctPriv priv = data->priv;
284    XctString enc = data->item + 6;
285    register XctString ptr = enc;
286    unsigned i, len;
287
288    while (*ptr != 0x02) {
289	if (!*ptr || (++ptr == priv->ptr))
290	    return 0;
291    }
292    data->item = ptr + 1;
293    data->item_length = priv->ptr - data->item;
294    len = ptr - enc;
295    for (i = 0;
296	 (i < priv->enc_count) &&
297	 strncmp(priv->encodings[i], (char *)enc, len);
298	 i++)
299	;
300    if (i == priv->enc_count) {
301	XctString cp;
302	char **new_encodings;
303
304	for (cp = enc; cp != ptr; cp++) {
305	    if ((!IsGL(*cp) && !IsGR(*cp)) || (*cp == 0x2a) || (*cp == 0x3f))
306		return 0;
307	}
308	ptr = malloc(len + 1);
309	memcpy(ptr, enc, len);
310	ptr[len] = 0x00;
311	priv->enc_count++;
312	new_encodings = reallocarray(priv->encodings,
313				     priv->enc_count, sizeof(char *));
314	if (new_encodings == NULL) {
315	    priv->enc_count--;
316	    free(ptr);
317	    return 0;
318	}
319	priv->encodings = new_encodings;
320	priv->encodings[i] = (char *)ptr;
321    }
322    data->encoding = priv->encodings[i];
323    data->char_size = c - 0x30;
324    return 1;
325}
326
327static void
328ShiftGRToGL(register XctData data, int hasCdata)
329{
330    register XctPriv priv = data->priv;
331    register int i;
332
333    if (data->item_length > priv->buf_count) {
334	priv->buf_count = data->item_length;
335	if (priv->itembuf)
336	    priv->itembuf = realloc(priv->itembuf, priv->buf_count);
337	else
338	    priv->itembuf = malloc(priv->buf_count);
339    }
340    memcpy(priv->itembuf, data->item, data->item_length);
341    data->item = priv->itembuf;
342    if (hasCdata) {
343	for (i = data->item_length; --i >= 0; ) {
344	    if (IsGR(data->item[i]))
345		data->item[i] &= 0x7f;
346	}
347    } else {
348	for (i = data->item_length; --i >= 0; )
349	    data->item[i] &= 0x7f;
350    }
351}
352
353/* Create an XctData structure for parsing a Compound Text string. */
354XctData
355XctCreate(_Xconst unsigned char *string, int length, XctFlags flags)
356{
357    register XctData data;
358    register XctPriv priv;
359
360    data = malloc(sizeof(struct _XctRec) + sizeof(struct _XctPriv));
361    if (!data)
362	return data;
363    data->priv = priv = (XctPriv)(data + 1);
364    data->total_string = (XctString)string;
365    data->total_length = length;
366    data->flags = flags;
367    priv->dirstack = (XctHDirection *)NULL;
368    priv->dirsize = 0;
369    priv->encodings = (char **)NULL;
370    priv->enc_count = 0;
371    priv->itembuf = (XctString)NULL;
372    priv->buf_count = 0;
373    XctReset(data);
374    return data;
375}
376
377/* Reset the XctData structure to re-parse the string from the beginning. */
378void
379XctReset(register XctData data)
380{
381    register XctPriv priv = data->priv;
382
383    priv->ptr = data->total_string;
384    priv->ptrend = data->total_string + data->total_length;
385    data->item = (XctString)NULL;
386    data->item_length = 0;
387    data->encoding = (char *)NULL;
388    data->char_size = 1;
389    data->horizontal = XctUnspecified;
390    data->horz_depth = 0;
391    priv->flags = 0;
392    data->GL_set_size = data->GR_set_size = 0; /* XXX */
393    (void)HandleGL(data, (unsigned char)0x42);
394    (void)Handle96GR(data, (unsigned char)0x41);
395    data->version = 1;
396    data->can_ignore_exts = 0;
397    /* parse version, if present */
398    if ((data->total_length >= 4) &&
399	(priv->ptr[0] == ESC) && (priv->ptr[1] == 0x23) &&
400	IsI2(priv->ptr[2]) &&
401	((priv->ptr[3] == 0x30) || (priv->ptr[3] == 0x31))) {
402	data->version = priv->ptr[2] - 0x1f;
403	if (priv->ptr[3] == 0x30)
404	    data->can_ignore_exts = 1;
405	priv->ptr += 4;
406    }
407}
408
409/* Parse the next "item" from the Compound Text string.  The return value
410 * indicates what kind of item is returned.  The item itself, and the current
411 * contextual state, are reported as components of the XctData structure.
412 */
413XctResult
414XctNextItem(register XctData data)
415{
416    register XctPriv priv = data->priv;
417    unsigned char c;
418    int len, bits;
419
420#define NEXT data->item_length++; priv->ptr++
421
422    while (IsMore(priv)) {
423	data->item = priv->ptr;
424	data->item_length = 0;
425	c = *priv->ptr;
426	if (c == ESC) {
427	    NEXT;
428	    while (IsMore(priv) && IsI2(*priv->ptr)) {
429		NEXT;
430	    }
431	    if (!IsMore(priv))
432		return XctError;
433	    c = *priv->ptr;
434	    NEXT;
435	    if (!IsESCF(c))
436		return XctError;
437	    switch (data->item[1]) {
438	    case 0x24:
439		if (data->item_length > 3) {
440		    if (data->item[2] == 0x28) {
441			if (HandleMultiGL(data, c))
442			    continue;
443		    } else if (data->item[2] == 0x29) {
444			if (HandleMultiGR(data, c))
445			    continue;
446		    }
447		}
448		break;
449	    case 0x25:
450		if ((data->item_length == 4) && (data->item[2] == 0x2f) &&
451		    (c <= 0x3f)) {
452		    if ((AmountLeft(priv) < 2) ||
453			(priv->ptr[0] < 0x80) || (priv->ptr[1] < 0x80))
454			return XctError;
455		    len = *priv->ptr - 0x80;
456		    NEXT;
457		    len = (len << 7) + (*priv->ptr - 0x80);
458		    NEXT;
459		    if (AmountLeft(priv) < len)
460			return XctError;
461		    data->item_length += len;
462		    priv->ptr += len;
463		    if (c <= 0x34) {
464			if (!HandleExtended(data, c) ||
465			    ((data->horz_depth == 0) &&
466			     (priv->flags & UsedDirection)))
467			    return XctError;
468			priv->flags |= UsedGraphic;
469			return XctExtendedSegment;
470		    }
471		}
472		break;
473	    case 0x28:
474		if (HandleGL(data, c))
475		    continue;
476		break;
477	    case 0x29:
478		if (Handle94GR(data, c))
479		    continue;
480		break;
481	    case 0x2d:
482		if (Handle96GR(data, c))
483		    continue;
484		break;
485	    }
486	} else if (c == CSI) {
487	    NEXT;
488	    while (IsMore(priv) && IsI3(*priv->ptr)) {
489		NEXT;
490	    }
491	    while (IsMore(priv) && IsI2(*priv->ptr)) {
492		NEXT;
493	    }
494	    if (!IsMore(priv))
495		return XctError;
496	    c = *priv->ptr;
497	    NEXT;
498	    if (!IsCSIF(c))
499		return XctError;
500	    if (c == 0x5d) {
501		if ((data->item_length == 3) &&
502		    ((data->item[1] == 0x31) || (data->item[1] == 0x32))) {
503		    data->horz_depth++;
504		    if (priv->dirsize < data->horz_depth) {
505			XctHDirection *new_dirstack;
506			priv->dirsize += 10;
507			new_dirstack = reallocarray(priv->dirstack,
508						    priv->dirsize,
509						    sizeof(XctHDirection));
510			if (new_dirstack == NULL) {
511			    priv->dirsize -= 10;
512			    return XctError;
513			}
514			priv->dirstack = new_dirstack;
515		    }
516		    priv->dirstack[data->horz_depth - 1] = data->horizontal;
517		    if (data->item[1] == 0x31)
518			data->horizontal = XctLeftToRight;
519		    else
520			data->horizontal = XctRightToLeft;
521		    if ((priv->flags & UsedGraphic) &&
522			!(priv->flags & UsedDirection))
523			return XctError;
524		    priv->flags |= UsedDirection;
525		    if (data->flags & XctHideDirection)
526			continue;
527		    return XctHorizontal;
528		} else if (data->item_length == 2) {
529		    if (!data->horz_depth)
530			return XctError;
531		    data->horz_depth--;
532		    data->horizontal = priv->dirstack[data->horz_depth];
533		    if (data->flags & XctHideDirection)
534			continue;
535		    return XctHorizontal;
536		}
537	    }
538	} else if (data->flags & XctSingleSetSegments) {
539	    NEXT;
540	    if IsC0(c) {
541		data->encoding = (char *)NULL;
542		data->char_size = 1;
543		if (IsLegalC0(data, c))
544		    return XctC0Segment;
545	    } else if (IsGL(c)) {
546		data->encoding = data->GL_encoding;
547		data->char_size = data->GL_char_size;
548		while (IsMore(priv) && IsGL(*priv->ptr)) {
549		    NEXT;
550		}
551		if (((data->char_size > 1) &&
552		     (data->item_length % data->char_size)) ||
553		    ((data->horz_depth == 0) &&
554		     (priv->flags & UsedDirection)))
555		    return XctError;
556		priv->flags |= UsedGraphic;
557		return XctGLSegment;
558	    } else if (IsC1(c)) {
559		data->encoding = (char *)NULL;
560		data->char_size = 1;
561		if (IsLegalC1(data, c))
562		    return XctC1Segment;
563	    } else {
564		data->encoding = data->GR_encoding;
565		data->char_size = data->GR_char_size;
566		while (IsMore(priv) && IsGR(*priv->ptr)) {
567		    NEXT;
568		}
569		if (((data->char_size > 1) &&
570		     (data->item_length % data->char_size)) ||
571		    ((data->horz_depth == 0) &&
572		     (priv->flags & UsedDirection)))
573		    return XctError;
574		priv->flags |= UsedGraphic;
575		if (!(priv->flags & ToGL))
576		    return XctGRSegment;
577		ShiftGRToGL(data, 0);
578		return XctGLSegment;
579	    }
580	} else {
581	    bits = 0;
582	    while (1) {
583		if (IsC0(c) || IsC1(c)) {
584		    if ((c == ESC) || (c == CSI))
585			break;
586		    if (IsC0(c) ? !IsLegalC0(data, c) : !IsLegalC1(data, c))
587			break;
588		    bits |= HasC;
589		    NEXT;
590		} else {
591		    len = data->item_length;
592		    if (IsGL(c)) {
593			if ((data->flags & XctShiftMultiGRToGL) &&
594			    (bits & HasGR))
595			    break;
596			NEXT;
597			bits |= HasGL;
598			while (IsMore(priv) && IsGL(*priv->ptr)) {
599			    NEXT;
600			}
601			if ((data->GL_char_size > 1) &&
602			    ((data->item_length - len) % data->GL_char_size))
603			    return XctError;
604		    } else {
605			if ((data->flags & XctShiftMultiGRToGL) &&
606			    (bits & HasGL))
607			    break;
608			NEXT;
609			bits |= HasGR;
610			while (IsMore(priv) && IsGR(*priv->ptr)) {
611			    NEXT;
612			}
613			if ((data->GR_char_size > 1) &&
614			    ((data->item_length - len) % data->GR_char_size))
615			    return XctError;
616		    }
617		}
618		if (!IsMore(priv))
619		    break;
620		c = *priv->ptr;
621	    }
622	    if (data->item_length) {
623		if (bits & (HasGL|HasGR)) {
624		    priv->flags |= UsedGraphic;
625		    if ((data->horz_depth == 0) &&
626			(priv->flags & UsedDirection))
627			return XctError;
628		    if ((data->flags & XctShiftMultiGRToGL) && (bits & HasGR))
629			ShiftGRToGL(data, bits & HasC);
630		}
631		if ((bits == (HasGL|HasGR)) ||
632		    (data->GLGR_encoding && !(bits & HasC))) {
633		    data->encoding = data->GLGR_encoding;
634		    if (data->GL_char_size == data->GR_char_size)
635			data->char_size = data->GL_char_size;
636		    else
637			data->char_size = 0;
638		} else if (bits == HasGL) {
639		    data->encoding = data->GL_encoding;
640		    data->char_size = data->GL_char_size;
641		} else if (bits == HasGR) {
642		    data->encoding = data->GR_encoding;
643		    data->char_size = data->GR_char_size;
644		} else {
645		    data->encoding = (char *)NULL;
646		    data->char_size = 1;
647		    if ((bits & HasGL) &&
648			(data->GL_char_size != data->char_size))
649			data->char_size = 0;
650		    if ((bits & HasGR) &&
651			(data->GR_char_size != data->char_size))
652			data->char_size = 0;
653		}
654		return XctSegment;
655	    }
656	    NEXT;
657	}
658	if (data->version <= XctVersion)
659	    return XctError;
660	if (data->flags & XctProvideExtensions)
661	    return XctExtension;
662	if (!data->can_ignore_exts)
663	    return XctError;
664    }
665    return XctEndOfText;
666}
667
668/* Free all data associated with an XctDataStructure. */
669void
670XctFree(register XctData data)
671{
672    unsigned i;
673    register XctPriv priv = data->priv;
674
675    if (priv->dirstack)
676	free(priv->dirstack);
677    if (data->flags & XctFreeString)
678	free(data->total_string);
679    for (i = 0; i < priv->enc_count; i++)
680	free(priv->encodings[i]);
681    if (priv->encodings)
682	free(priv->encodings);
683    if (priv->itembuf)
684	free(priv->itembuf);
685    free(data);
686}
687