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