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