cmsColNm.c revision 57f47464
1
2/*
3 * Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc.
4 * 	All Rights Reserved
5 *
6 * This file is a component of an X Window System-specific implementation
7 * of Xcms based on the TekColor Color Management System.  Permission is
8 * hereby granted to use, copy, modify, sell, and otherwise distribute this
9 * software and its documentation for any purpose and without fee, provided
10 * that this copyright, permission, and disclaimer notice is reproduced in
11 * all copies of this software and in supporting documentation.  TekColor
12 * is a trademark of Tektronix, Inc.
13 *
14 * Tektronix makes no representation about the suitability of this software
15 * for any purpose.  It is provided "as is" and with all faults.
16 *
17 * TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE,
18 * INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 * PARTICULAR PURPOSE.  IN NO EVENT SHALL TEKTRONIX BE LIABLE FOR ANY
20 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
21 * RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF
22 * CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
23 * CONNECTION WITH THE USE OR THE PERFORMANCE OF THIS SOFTWARE.
24 *
25 *	NAME
26 *		XcmsColNm.c
27 *
28 *	DESCRIPTION
29 *		Source for _XcmsLookupColorName().
30 *
31 *
32 */
33
34#ifdef HAVE_CONFIG_H
35#include <config.h>
36#endif
37#include "Xlibint.h"
38#include "Xcmsint.h"
39#include <X11/Xos.h>
40#include <sys/stat.h>
41#include <stdio.h>
42#include <ctype.h>
43#define XK_LATIN1
44#include <X11/keysymdef.h>
45#include "Cv.h"
46
47/* forwards/locals */
48static Status LoadColornameDB(void);
49
50
51/*
52 *      LOCAL DEFINES
53 *		#define declarations local to this package.
54 */
55#ifndef XCMSDB
56#define XCMSDB  "/usr/lib/X11/Xcms.txt"
57#endif
58
59#ifndef isgraph
60#  define isgraph(c)	(isprint((c)) && !isspace((c)))
61#endif
62
63#ifndef XCMSDB_MAXLINELEN
64#  define XCMSDB_MAXLINELEN	256
65#endif
66
67#define FORMAT_VERSION	"0.1"
68#define START_TOKEN	"XCMS_COLORDB_START"
69#define END_TOKEN	"XCMS_COLORDB_END"
70#define DELIM_CHAR	'\t'
71
72#define	NOT_VISITED	0x0
73#define	VISITED		0x1
74#define CYCLE		0xFFFF
75#define XcmsDbInitNone		-1
76#define XcmsDbInitFailure	0
77#define XcmsDbInitSuccess	1
78
79/*
80 *      LOCAL TYPEDEFS
81 */
82typedef struct _XcmsPair {
83    const char *first;
84    const char *second;
85    int flag;
86} XcmsPair;
87
88/*
89 *      LOCAL VARIABLES
90 */
91static int XcmsColorDbState = XcmsDbInitNone;
92static int nEntries;
93static char *strings;
94static XcmsPair *pairs;
95static const char whitePtStr[] = "WhitePoint";
96
97
98/************************************************************************
99 *									*
100 *			PRIVATE ROUTINES				*
101 *									*
102 ************************************************************************/
103
104/*
105 *	NAME
106 *		_XcmsColorSpaceOfString
107 *
108 *	SYNOPSIS
109 */
110static XcmsColorSpace *
111_XcmsColorSpaceOfString(
112    XcmsCCC ccc,
113    const char *color_string)
114/*
115 *	DESCRIPTION
116 *		Returns a pointer to the color space structure
117 *		(XcmsColorSpace) associated with the specified color string.
118 *
119 *	RETURNS
120 *		Pointer to matching XcmsColorSpace structure if found;
121 *		otherwise NULL.
122 *
123 *	CAVEATS
124 *
125 */
126{
127    XcmsColorSpace	**papColorSpaces;
128    int n;
129    char *pchar;
130
131    if ((pchar = strchr(color_string, ':')) == NULL) {
132	return(XcmsFailure);
133    }
134    n = (int)(pchar - color_string);
135
136    if (ccc == NULL) {
137	return(NULL);
138    }
139
140    /*
141     * First try Device-Independent color spaces
142     */
143    papColorSpaces = _XcmsDIColorSpaces;
144    if (papColorSpaces != NULL) {
145	while (*papColorSpaces != NULL) {
146	    if (strncmp((*papColorSpaces)->prefix, color_string, n) == 0 &&
147		!((*papColorSpaces)->prefix)[n]) {
148		return(*papColorSpaces);
149	    }
150	    papColorSpaces++;
151	}
152    }
153
154    /*
155     * Next try Device-Dependent color spaces
156     */
157    papColorSpaces = ((XcmsFunctionSet *)ccc->pPerScrnInfo->functionSet)->DDColorSpaces;
158    if (papColorSpaces != NULL) {
159	while (*papColorSpaces != NULL) {
160	    if (strncmp((*papColorSpaces)->prefix, color_string, n) == 0 &&
161		!((*papColorSpaces)->prefix)[n]) {
162		return(*papColorSpaces);
163	    }
164	    papColorSpaces++;
165	}
166    }
167
168    return(NULL);
169}
170
171
172/*
173 *	NAME
174 *		_XcmsParseColorString
175 *
176 *	SYNOPSIS
177 */
178static int
179_XcmsParseColorString(
180    XcmsCCC ccc,
181    const char *color_string,
182    XcmsColor *pColor)
183/*
184 *	DESCRIPTION
185 *		Assuming color_string contains a numerical string color
186 *		specification, attempts to parse a string into an
187 *		XcmsColor structure.
188 *
189 *	RETURNS
190 *		0 if failed; otherwise non-zero.
191 *
192 *	CAVEATS
193 *		A color string containing a numerical color specification
194 *		must be in ISO Latin-1 encoding!
195 */
196{
197    XcmsColorSpace	*pColorSpace;
198    char		string_buf[64];
199    char		*string_lowered;
200    int			len;
201    int			res;
202
203    if (ccc == NULL) {
204	return(0);
205    }
206
207    /*
208     * While copying color_string to string_lowered, convert to lowercase
209     */
210    if ((len = strlen(color_string)) >= sizeof(string_buf)) {
211	string_lowered = (char *) Xmalloc(len+1);
212    } else {
213	string_lowered = string_buf;
214    }
215
216    _XcmsCopyISOLatin1Lowered(string_lowered, color_string);
217
218    if (*string_lowered == '#') {
219	if ((pColorSpace = _XcmsColorSpaceOfString(ccc, "rgb:")) != NULL) {
220	    res = (*pColorSpace->parseString)(string_lowered, pColor);
221	    if (len >= sizeof(string_buf)) Xfree(string_lowered);
222	    return res;
223	}
224    }
225
226    if ((pColorSpace = _XcmsColorSpaceOfString(ccc, string_lowered)) != NULL) {
227	res = (*pColorSpace->parseString)(string_lowered, pColor);
228	if (len >= sizeof(string_buf)) Xfree(string_lowered);
229	return res;
230    }
231
232    if (len >= sizeof(string_buf)) Xfree(string_lowered);
233    return(0);
234}
235
236
237/*
238 *	NAME
239 *		FirstCmp - Compare color names of pair recs
240 *
241 *	SYNOPSIS
242 */
243static int
244FirstCmp(const void *p1, const void *p2)
245/*
246 *	DESCRIPTION
247 *		Compares the color names of XcmsColorTuples.
248 *		This routine is public to allow access from qsort???.
249 *
250 *	RETURNS
251 *		0 if equal;
252 *		< 0 if first precedes second,
253 *		> 0 if first succeeds second.
254 *
255 */
256{
257    return(strcmp(((XcmsPair *)p1)->first, ((XcmsPair *)p2)->first));
258}
259
260
261
262/*
263 *	NAME
264 *		stringSectionSize - determine memory needed for strings
265 *
266 *	SYNOPSIS
267 */
268static void
269SetNoVisit(void)
270/*
271 *	DESCRIPTION
272 *
273 *	RETURNS
274 *		void
275 *
276 */
277{
278    int i;
279    XcmsPair *pair = pairs;
280
281    for (i = 0; i < nEntries; i++, pair++) {
282	if (pair->flag != CYCLE) {
283	    pair->flag = NOT_VISITED;
284	}
285    }
286}
287
288
289
290
291/*
292 *	NAME
293 *		field2 - extract two fields
294 *
295 *	SYNOPSIS
296 */
297static int
298field2(
299    char *pBuf,
300    char delim,	/* in:  field delimiter */
301    char **p1,	/* in/out: pointer to pointer to field 1 */
302    char **p2)	/* in/out: pointer to pointer to field 2 */
303/*
304 *	DESCRIPTION
305 *		Extracts two fields from a "record".
306 *
307 *	RETURNS
308 *		XcmsSuccess if succeeded, otherwise XcmsFailure.
309 *
310 */
311{
312    *p1 = *p2 = NULL;
313
314    /* Find Field 1 */
315    while (!isgraph(*pBuf)) {
316	if ((*pBuf != '\n') || (*pBuf != '\0')) {
317	    return(XcmsFailure);
318	}
319	if (isspace(*pBuf) || (*pBuf == delim)) {
320	    pBuf++;
321	}
322    }
323    *p1 = pBuf;
324
325    /* Find end of Field 2 */
326    while (isprint(*pBuf) && (*pBuf != delim)) {
327	pBuf++;
328    }
329    if ((*pBuf == '\n') || (*pBuf == '\0')) {
330	return(XcmsFailure);
331    }
332    if ((*pBuf == ' ') || (*pBuf == delim)) {
333	*pBuf++ = '\0';	/* stuff end of string character */
334    } else {
335	return(XcmsFailure);
336    }
337
338    /* Find Field 2 */
339    while (!isgraph(*pBuf)) {
340	if ((*pBuf == '\n') || (*pBuf == '\0')) {
341	    return(XcmsFailure);
342	}
343	if (isspace(*pBuf) || (*pBuf == delim)) {
344	    pBuf++;
345	}
346    }
347    *p2 = pBuf;
348
349    /* Find end of Field 2 */
350    while (isprint(*pBuf) && (*pBuf != delim)) {
351	pBuf++;
352    }
353    if (*pBuf != '\0') {
354	*pBuf = '\0';	/* stuff end of string character */
355    }
356
357    return(XcmsSuccess);
358}
359
360
361/*
362 *	NAME
363 *		_XcmsLookupColorName - Lookup DB entry for a color name
364 *
365 *	SYNOPSIS
366 */
367static Status
368_XcmsLookupColorName(
369    XcmsCCC ccc,
370    const char **name,
371    XcmsColor *pColor)
372/*
373 *	DESCRIPTION
374 *		Searches for an entry in the Device-Independent Color Name
375 *		Database for the specified string.
376 *
377 *	RETURNS
378 *		XcmsFailure if failed to find a matching entry in
379 *			the database.
380 *		XcmsSuccess if succeeded in converting color name to
381 *			XcmsColor.
382 *		_XCMS_NEWNAME if succeeded in converting color string (which
383 *			is a color name to yet another color name.  Note
384 *			that the new name is passed back via 'name'.
385 */
386 {
387    Status		retval = 0;
388    char		name_lowered_64[64];
389    char		*name_lowered;
390    register int	i, j, left, right;
391    int			len;
392    const char		*tmpName;
393    XcmsPair		*pair = NULL;
394
395    /*
396     * Check state of Database:
397     *		XcmsDbInitNone
398     *		XcmsDbInitSuccess
399     *		XcmsDbInitFailure
400     */
401    if (XcmsColorDbState == XcmsDbInitFailure) {
402	return(XcmsFailure);
403    }
404    if (XcmsColorDbState == XcmsDbInitNone) {
405	if (!LoadColornameDB()) {
406	    return(XcmsFailure);
407	}
408    }
409
410    SetNoVisit();
411
412    /*
413     * While copying name to name_lowered, convert to lowercase
414     */
415
416    tmpName = *name;
417
418Retry:
419    if ((len = strlen(tmpName)) > 63) {
420	name_lowered = (char *) Xmalloc(len+1);
421    } else {
422	name_lowered = name_lowered_64;
423    }
424
425    _XcmsCopyISOLatin1Lowered(name_lowered, tmpName);
426
427    /*
428     * Now, remove spaces.
429     */
430    for (i = 0, j = 0; j < len; j++) {
431	if (!isspace(name_lowered[j])) {
432	    name_lowered[i++] = name_lowered[j];
433	}
434    }
435    name_lowered[i] = '\0';
436
437    left = 0;
438    right = nEntries - 1;
439    while (left <= right) {
440	i = (left + right) >> 1;
441	pair = &pairs[i];
442	j = strcmp(name_lowered, pair->first);
443	if (j < 0)
444	    right = i - 1;
445	else if (j > 0)
446	    left = i + 1;
447	else {
448	    break;
449	}
450    }
451    if (len > 63) Xfree(name_lowered);
452
453    if (left > right) {
454	if (retval == 2) {
455	    if (*name != tmpName) {
456		*name = tmpName;
457	    }
458	    return(_XCMS_NEWNAME);
459	}
460	return(XcmsFailure);
461    }
462
463    if (pair->flag == CYCLE) {
464	return(XcmsFailure);
465    }
466    if (pair->flag == VISITED) {
467	pair->flag = CYCLE;
468	return(XcmsFailure);
469    }
470
471    if (_XcmsParseColorString(ccc, pair->second, pColor) == XcmsSuccess) {
472	/* f2 contains a numerical string specification */
473	return(XcmsSuccess);
474    } else {
475	/* f2 does not contain a numerical string specification */
476	tmpName = pair->second;
477	pair->flag = VISITED;
478	retval = 2;
479	goto Retry;
480    }
481}
482
483
484/*
485 *	NAME
486 *		RemoveSpaces
487 *
488 *	SYNOPSIS
489 */
490static int
491RemoveSpaces(
492    char *pString)
493/*
494 *	DESCRIPTION
495 *		Removes spaces from string.
496 *
497 *	RETURNS
498 *		Void
499 *
500 */
501{
502    int i, count = 0;
503    char *cptr;
504
505    /* REMOVE SPACES */
506    cptr = pString;
507    for (i = strlen(pString); i; i--, cptr++) {
508	if (!isspace(*cptr)) {
509	    *pString++ = *cptr;
510	    count++;
511	}
512    }
513    *pString = '\0';
514    return(count);
515}
516
517
518/*
519 *	NAME
520 *		stringSectionSize - determine memory needed for strings
521 *
522 *	SYNOPSIS
523 */
524static int
525stringSectionSize(
526    FILE *stream,
527    int	*pNumEntries,
528    int	*pSectionSize)
529/*
530 *	DESCRIPTION
531 *		Determines the amount of memory required to store the
532 *		color name strings and also the number of strings.
533 *
534 *	RETURNS
535 *		XcmsSuccess if succeeded, otherwise XcmsFailure.
536 *
537 */
538{
539    char buf[XCMSDB_MAXLINELEN];
540    char token[XCMSDB_MAXLINELEN];
541    char token2[XCMSDB_MAXLINELEN];
542    char *pBuf;
543    char *f1;
544    char *f2;
545    int i;
546
547    *pNumEntries = 0;
548    *pSectionSize = 0;
549
550    /*
551     * Advance to START_TOKEN
552     *	 Anything before is just considered as comments.
553     */
554
555    while((pBuf = fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
556	if ((sscanf(buf, "%s %s", token, token2))
557		&& (strcmp(token, START_TOKEN) == 0)) {
558	    if (strcmp(token2, FORMAT_VERSION) != 0) {
559		/* text file not in the right format */
560		return(XcmsFailure);
561	    }
562	    break;
563	} /* else it was just a blank line or comment */
564    }
565
566    if (pBuf == NULL) {
567	return(XcmsFailure);
568    }
569
570    while((fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
571	if ((sscanf(buf, "%s", token)) && (strcmp(token, END_TOKEN) == 0)) {
572	    break;
573	}
574
575	if (field2(buf, DELIM_CHAR, &f1, &f2) != XcmsSuccess) {
576	    return(XcmsFailure);
577	}
578
579	(*pNumEntries)++;
580
581	(*pSectionSize) += (i = strlen(f1)) + 1;
582	for (; i; i--, f1++) {
583	    /* REMOVE SPACES FROM COUNT */
584	    if (isspace(*f1)) {
585		(*pSectionSize)--;
586	    }
587	}
588
589	(*pSectionSize) += (i = strlen(f2)) + 1;
590	for (; i; i--, f2++) {
591	    /* REMOVE SPACES FROM COUNT */
592	    if (isspace(*f2)) {
593		(*pSectionSize)--;
594	    }
595	}
596
597    }
598
599    return(XcmsSuccess);
600}
601
602
603/*
604 *	NAME
605 *		ReadColornameDB - Read the Color Name Database
606 *
607 *	SYNOPSIS
608 */
609static Status
610ReadColornameDB(
611    FILE *stream,
612    XcmsPair *pRec,
613    char *pString)
614/*
615 *	DESCRIPTION
616 *		Loads the Color Name Database from a text file.
617 *
618 *	RETURNS
619 *		XcmsSuccess if succeeded, otherwise XcmsFailure.
620 *
621 */
622{
623    char buf[XCMSDB_MAXLINELEN];
624    char token[XCMSDB_MAXLINELEN];
625    char token2[XCMSDB_MAXLINELEN];
626    char *f1;
627    char *f2;
628    char *pBuf;
629
630    /*
631     * Advance to START_TOKEN
632     *	 Anything before is just considered as comments.
633     */
634
635    while((pBuf = fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
636	if ((sscanf(buf, "%s %s", token, token2))
637		&& (strcmp(token, START_TOKEN) == 0)) {
638	    if (strcmp(token2, FORMAT_VERSION) != 0) {
639		/* text file not in the right format */
640		return(XcmsFailure);
641	    }
642	    break;
643	} /* else it was just a blank line or comment */
644    }
645
646    if (pBuf == NULL) {
647	return(XcmsFailure);
648    }
649
650    /*
651     * Process lines between START_TOKEN to END_TOKEN
652     */
653
654    while ((fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
655	if ((sscanf(buf, "%s", token)) && (strcmp(token, END_TOKEN) == 0)) {
656	    /*
657	     * Found END_TOKEN so break out of for loop
658	     */
659	    break;
660	}
661
662	/*
663	 * Get pairs
664	 */
665	if (field2(buf, DELIM_CHAR, &f1, &f2) != XcmsSuccess) {
666	    /* Invalid line */
667	    continue;
668	}
669
670	/*
671	 * Add strings
672	 */
673
674	/* Left String */
675	pRec->first = pString;
676	_XcmsCopyISOLatin1Lowered(pString, f1);
677	pString += (1 + RemoveSpaces(pString));
678	pRec->second = pString;
679	/* Right String */
680	_XcmsCopyISOLatin1Lowered(pString, f2);
681	pString += RemoveSpaces(pString) + 1;
682	pRec++;
683
684    }
685
686    return(XcmsSuccess);
687}
688
689
690/*
691 *	NAME
692 *		LoadColornameDB - Load the Color Name Database
693 *
694 *	SYNOPSIS
695 */
696static Status
697LoadColornameDB(void)
698/*
699 *	DESCRIPTION
700 *		Loads the Color Name Database from a text file.
701 *
702 *	RETURNS
703 *		XcmsSuccess if succeeded, otherwise XcmsFailure.
704 *
705 */
706{
707    int size;
708    FILE *stream;
709    const char *pathname;
710    struct stat txt;
711    int length;
712
713    /* use and name of this env var is not part of the standard */
714    /* implementation-dependent feature */
715    if ((pathname = getenv("XCMSDB")) == NULL) {
716	pathname = XCMSDB;
717    }
718#ifdef __UNIXOS2__
719    pathname = __XOS2RedirRoot(pathname);
720#endif
721
722    length = strlen(pathname);
723    if ((length == 0) || (length >= (BUFSIZ - 5))){
724	XcmsColorDbState = XcmsDbInitFailure;
725	return(XcmsFailure);
726    }
727
728    if (stat(pathname, &txt)) {
729	/* can't stat file */
730	XcmsColorDbState = XcmsDbInitFailure;
731	return(XcmsFailure);
732    }
733
734    if ((stream = _XFopenFile (pathname, "r")) == NULL) {
735	/* can't open file */
736	XcmsColorDbState = XcmsDbInitFailure;
737	return(XcmsFailure);
738    }
739
740    if (stringSectionSize(stream, &nEntries, &size) != XcmsSuccess ||
741	nEntries == 0) {
742	(void) fclose(stream);
743	XcmsColorDbState = XcmsDbInitFailure;
744	return(XcmsFailure);
745    }
746    rewind(stream);
747
748    strings = (char *) Xmalloc(size);
749    pairs = (XcmsPair *)Xcalloc(nEntries, sizeof(XcmsPair));
750
751    ReadColornameDB(stream, pairs, strings);
752    (void) fclose(stream);
753
754    /*
755     * sort the pair recs
756     */
757    qsort((char *)pairs, nEntries, sizeof(XcmsPair), FirstCmp);
758
759    XcmsColorDbState = XcmsDbInitSuccess;
760    return(XcmsSuccess);
761}
762
763
764/************************************************************************
765 *									*
766 *			API PRIVATE ROUTINES				*
767 *									*
768 ************************************************************************/
769
770/*
771 *	NAME
772 *		_XcmsCopyISOLatin1Lowered
773 *
774 *	SYNOPSIS
775 */
776void
777_XcmsCopyISOLatin1Lowered(
778    char *dst,
779    const char *src)
780/*
781 *	DESCRIPTION
782 *		ISO Latin-1 case conversion routine
783 *		Identical to XmuCopyISOLatin1Lowered() but provided here
784 *		to eliminate need to link with libXmu.a.
785 *
786 *		IMPLEMENTORS NOTE:
787 *		    This routine is also used in XcmsFormatOfPrefix.
788 *
789 *	RETURNS
790 *		Void
791 *
792 */
793{
794    register unsigned char *dest;
795    register const unsigned char *source;
796
797    for (dest = (unsigned char *)dst, source = (const unsigned char *)src;
798	 *source;
799	 source++, dest++)
800    {
801	if ((*source >= XK_A) && (*source <= XK_Z))
802	    *dest = *source + (XK_a - XK_A);
803	else if ((*source >= XK_Agrave) && (*source <= XK_Odiaeresis))
804	    *dest = *source + (XK_agrave - XK_Agrave);
805	else if ((*source >= XK_Ooblique) && (*source <= XK_Thorn))
806	    *dest = *source + (XK_oslash - XK_Ooblique);
807	else
808	    *dest = *source;
809    }
810    *dest = '\0';
811}
812
813
814/*
815 *	NAME
816 *		_XcmsResolveColorString -
817 *
818 *	SYNOPSIS
819 */
820Status
821_XcmsResolveColorString (
822    XcmsCCC ccc,
823    const char **color_string,
824    XcmsColor *pColor_exact_return,
825    XcmsColorFormat result_format)
826/*
827 *	DESCRIPTION
828 *		The XcmsLookupColor function finds the color specification
829 *		associated with a color name in the Device-Independent Color
830 *		Name Database.
831 *	RETURNS
832 *		XcmsFailure if failed to convert valid color string.
833 *		XcmsSuccess if succeeded in converting color string to
834 *			XcmsColor.
835 *		_XCMS_NEWNAME if failed to parse the string or find it in
836 *			the database, or if succeeded in looking it up and
837 *			found another name which is not in the database.
838 *			Note that the new name is returned in color_string.
839 *
840 *		This function returns both the color specification found in the
841 *		database (db specification) and the color specification for the
842 *		color displayable by the specified screen (screen
843 *		specification).  The calling routine sets the format for these
844 *		returned specifications in the XcmsColor format component.
845 *		If XcmsUndefinedFormat, the specification is returned in the
846 *		format used to store the color in the database.
847 */
848{
849    XcmsColor dbWhitePt;	/* whitePt associated with pColor_exact_return*/
850				/*    the screen's white point */
851    XcmsColor *pClientWhitePt;
852    int retval;
853    const char *strptr = whitePtStr;
854
855/*
856 * 0. Check for invalid arguments.
857 */
858    if (ccc == NULL || (*color_string)[0] == '\0' || pColor_exact_return == NULL) {
859	return(XcmsFailure);
860    }
861
862/*
863 * 1. First attempt to parse the string
864 *    If successful, then convert the specification to the target format
865 *    and return.
866 */
867    if (_XcmsParseColorString(ccc, *color_string, pColor_exact_return)
868	    == 1) {
869	if (result_format != XcmsUndefinedFormat
870		&& pColor_exact_return->format != result_format) {
871	    /* need to be converted to the target format */
872	    return(XcmsConvertColors(ccc, pColor_exact_return, 1,
873		    result_format, (Bool *)NULL));
874	} else {
875	    return(XcmsSuccess);
876	}
877    }
878
879/*
880 * 2. Attempt to find it in the DI Color Name Database
881 */
882
883    /*
884     * a. Convert String into a XcmsColor structure
885     *       Attempt to extract the specification for color_string from the
886     *       DI Database (pColor_exact_return).  If the DI Database does not
887     *	     have this entry, then return failure.
888     */
889    retval = _XcmsLookupColorName(ccc, color_string, pColor_exact_return);
890
891    if (retval != XcmsSuccess) {
892	/* color_string replaced with a color name, or not found */
893	return(_XCMS_NEWNAME);
894    }
895
896    if (pColor_exact_return->format == XcmsUndefinedFormat) {
897	return(XcmsFailure);
898    }
899
900    /*
901     * b. If result_format not defined, then assume target format
902     *	  is the exact format.
903     */
904    if (result_format == XcmsUndefinedFormat) {
905	result_format = pColor_exact_return->format;
906    }
907
908    if ((ClientWhitePointOfCCC(ccc))->format == XcmsUndefinedFormat) {
909	pClientWhitePt = ScreenWhitePointOfCCC(ccc);
910    } else {
911	pClientWhitePt = ClientWhitePointOfCCC(ccc);
912    }
913
914    /*
915     * c. Convert to the target format, making adjustments for white
916     *	  point differences as necessary.
917     */
918    if (XCMS_DD_ID(pColor_exact_return->format)) {
919	/*
920	 * The spec format is Device-Dependent, therefore assume the
921	 *    its white point is the Screen White Point.
922	 */
923	if (XCMS_DD_ID(result_format)) {
924	    /*
925	     * Target format is Device-Dependent
926	     *	Therefore, DD --> DD conversion
927	     */
928	    return(_XcmsDDConvertColors(ccc, pColor_exact_return,
929		    1, result_format, (Bool *) NULL));
930	} else {
931	    /*
932	     * Target format is Device-Independent
933	     *	Therefore, DD --> DI conversion
934	     */
935	    if (ccc->whitePtAdjProc && !_XcmsEqualWhitePts(ccc,
936		    pClientWhitePt, ScreenWhitePointOfCCC(ccc))) {
937		return((*ccc->whitePtAdjProc)(ccc, ScreenWhitePointOfCCC(ccc),
938			pClientWhitePt, result_format,
939			pColor_exact_return, 1, (Bool *) NULL));
940	    } else {
941		if (_XcmsDDConvertColors(ccc, pColor_exact_return, 1,
942			XcmsCIEXYZFormat, (Bool *) NULL) == XcmsFailure) {
943		    return(XcmsFailure);
944		}
945		return(_XcmsDIConvertColors(ccc, pColor_exact_return,
946			pClientWhitePt, 1, result_format));
947	    }
948	}
949    } else {
950	/*
951	 * The spec format is Device-Independent, therefore attempt
952	 * to find a database white point.
953	 *
954	 * If the Database does not have a white point, then assume the
955	 * database white point is the same as the Screen White Point.
956	 */
957
958	if (_XcmsLookupColorName(ccc, &strptr, &dbWhitePt) != 1) {
959	    memcpy((char *)&dbWhitePt,
960		   (char *)&ccc->pPerScrnInfo->screenWhitePt,
961		   sizeof(XcmsColor));
962	}
963	if (XCMS_DD_ID(result_format)) {
964	    /*
965	     * Target format is Device-Dependent
966	     *	Therefore, DI --> DD conversion
967	     */
968	    if (ccc->whitePtAdjProc && !_XcmsEqualWhitePts(ccc,
969		    &dbWhitePt, ScreenWhitePointOfCCC(ccc))) {
970		return((*ccc->whitePtAdjProc)(ccc, &dbWhitePt,
971			ScreenWhitePointOfCCC(ccc), result_format,
972			pColor_exact_return, 1, (Bool *)NULL));
973	    } else {
974		if (pColor_exact_return->format != XcmsCIEXYZFormat) {
975		    if (_XcmsDIConvertColors(ccc, pColor_exact_return,
976			    &dbWhitePt, 1, XcmsCIEXYZFormat) == XcmsFailure) {
977			return(XcmsFailure);
978		    }
979		}
980		return (_XcmsDDConvertColors(ccc, pColor_exact_return, 1,
981			result_format, (Bool *)NULL));
982	    }
983	} else {
984	    /*
985	     * Target format is Device-Independent
986	     *	Therefore, DI --> DI conversion
987	     */
988	    if (ccc->whitePtAdjProc && !_XcmsEqualWhitePts(ccc,
989		    &dbWhitePt, pClientWhitePt)) {
990		/*
991		 * The calling routine wants to resolve this color
992		 * in terms if it's white point (i.e. Client White Point).
993		 * Therefore, apply white adjustment for the displacement
994		 * between dbWhitePt to clientWhitePt.
995		 */
996		return((*ccc->whitePtAdjProc)(ccc, &dbWhitePt,
997			pClientWhitePt, result_format,
998			pColor_exact_return, 1, (Bool *)NULL));
999	    } else if (_XcmsEqualWhitePts(ccc,
1000		    &dbWhitePt, pClientWhitePt)) {
1001		/*
1002		 * Can use either dbWhitePt or pClientWhitePt to
1003		 * convert to the result_format.
1004		 */
1005		if (pColor_exact_return->format == result_format) {
1006		    return(XcmsSuccess);
1007		} else {
1008		    return (_XcmsDIConvertColors(ccc, pColor_exact_return,
1009			    &dbWhitePt, 1, result_format));
1010		}
1011	    } else {
1012		/*
1013		 * Need to convert to a white point independent color
1014		 * space (let's choose CIEXYZ) then convert to the
1015		 * target color space.  Why? Lets assume that
1016		 * pColor_exact_return->format and result format
1017		 * are white point dependent format (e.g., CIELUV, CIELAB,
1018		 * TekHVC ... same or any combination). If so, we'll
1019		 * need to convert the color with dbWhitePt to an absolute
1020		 * spec (i.e.  non-white point dependent) then convert that
1021		 * absolute value with clientWhitePt to the result_format.
1022		 */
1023		if (pColor_exact_return->format != XcmsCIEXYZFormat) {
1024		    if (_XcmsDIConvertColors(ccc, pColor_exact_return,
1025			    &dbWhitePt, 1, XcmsCIEXYZFormat) == XcmsFailure) {
1026			return(XcmsFailure);
1027		    }
1028		}
1029		if (result_format == XcmsCIEXYZFormat) {
1030		    return(XcmsSuccess);
1031		} else {
1032		    return(_XcmsDIConvertColors(ccc, pColor_exact_return,
1033			    pClientWhitePt, 1, result_format));
1034		}
1035	    }
1036	}
1037    }
1038}
1039