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    size_t n;
130    char *pchar;
131
132    if ((pchar = strchr(color_string, ':')) == NULL) {
133	return(XcmsFailure);
134    }
135    n = (size_t)(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    size_t		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	if (string_lowered == NULL)
214	    return(XcmsFailure);
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 *
252 *	RETURNS
253 *		0 if equal;
254 *		< 0 if first precedes second,
255 *		> 0 if first succeeds second.
256 *
257 */
258{
259    return(strcmp(((const XcmsPair *)p1)->first, ((const XcmsPair *)p2)->first));
260}
261
262
263
264/*
265 *	NAME
266 *		stringSectionSize - determine memory needed for strings
267 *
268 *	SYNOPSIS
269 */
270static void
271SetNoVisit(void)
272/*
273 *	DESCRIPTION
274 *
275 *	RETURNS
276 *		void
277 *
278 */
279{
280    int i;
281    XcmsPair *pair = pairs;
282
283    for (i = 0; i < nEntries; i++, pair++) {
284	if (pair->flag != CYCLE) {
285	    pair->flag = NOT_VISITED;
286	}
287    }
288}
289
290
291
292
293/*
294 *	NAME
295 *		field2 - extract two fields
296 *
297 *	SYNOPSIS
298 */
299static int
300field2(
301    char *pBuf,
302    char delim,	/* in:  field delimiter */
303    char **p1,	/* in/out: pointer to pointer to field 1 */
304    char **p2)	/* in/out: pointer to pointer to field 2 */
305/*
306 *	DESCRIPTION
307 *		Extracts two fields from a "record".
308 *
309 *	RETURNS
310 *		XcmsSuccess if succeeded, otherwise XcmsFailure.
311 *
312 */
313{
314    *p1 = *p2 = NULL;
315
316    /* Find Field 1 */
317    while (!isgraph(*pBuf)) {
318	if ((*pBuf == '\n') || (*pBuf == '\0')) {
319	    return(XcmsFailure);
320	}
321	if (isspace(*pBuf) || (*pBuf == delim)) {
322	    pBuf++;
323	}
324    }
325    *p1 = pBuf;
326
327    /* Find end of Field 2 */
328    while (isprint(*pBuf) && (*pBuf != delim)) {
329	pBuf++;
330    }
331    if ((*pBuf == '\n') || (*pBuf == '\0')) {
332	return(XcmsFailure);
333    }
334    if ((*pBuf == ' ') || (*pBuf == delim)) {
335	*pBuf++ = '\0';	/* stuff end of string character */
336    } else {
337	return(XcmsFailure);
338    }
339
340    /* Find Field 2 */
341    while (!isgraph(*pBuf)) {
342	if ((*pBuf == '\n') || (*pBuf == '\0')) {
343	    return(XcmsFailure);
344	}
345	if (isspace(*pBuf) || (*pBuf == delim)) {
346	    pBuf++;
347	}
348    }
349    *p2 = pBuf;
350
351    /* Find end of Field 2 */
352    while (isprint(*pBuf) && (*pBuf != delim)) {
353	pBuf++;
354    }
355    if (*pBuf != '\0') {
356	*pBuf = '\0';	/* stuff end of string character */
357    }
358
359    return(XcmsSuccess);
360}
361
362
363/*
364 *	NAME
365 *		_XcmsLookupColorName - Lookup DB entry for a color name
366 *
367 *	SYNOPSIS
368 */
369static Status
370_XcmsLookupColorName(
371    XcmsCCC ccc,
372    const char **name,
373    XcmsColor *pColor)
374/*
375 *	DESCRIPTION
376 *		Searches for an entry in the Device-Independent Color Name
377 *		Database for the specified string.
378 *
379 *	RETURNS
380 *		XcmsFailure if failed to find a matching entry in
381 *			the database.
382 *		XcmsSuccess if succeeded in converting color name to
383 *			XcmsColor.
384 *		_XCMS_NEWNAME if succeeded in converting color string (which
385 *			is a color name to yet another color name.  Note
386 *			that the new name is passed back via 'name'.
387 */
388 {
389    Status		retval = 0;
390    char		name_lowered_64[64];
391    char		*name_lowered;
392    register int	i, j, left, right;
393    int			len;
394    const char		*tmpName;
395    XcmsPair		*pair = NULL;
396
397    /*
398     * Check state of Database:
399     *		XcmsDbInitNone
400     *		XcmsDbInitSuccess
401     *		XcmsDbInitFailure
402     */
403    if (XcmsColorDbState == XcmsDbInitFailure) {
404	return(XcmsFailure);
405    }
406    if (XcmsColorDbState == XcmsDbInitNone) {
407	if (!LoadColornameDB()) {
408	    return(XcmsFailure);
409	}
410    }
411
412    SetNoVisit();
413
414    /*
415     * While copying name to name_lowered, convert to lowercase
416     */
417
418    tmpName = *name;
419
420Retry:
421    if ((len = (int)strlen(tmpName)) > 63) {
422	name_lowered = Xmalloc(len+1);
423	if (name_lowered == NULL)
424	    return(XcmsFailure);
425    } else {
426	name_lowered = name_lowered_64;
427    }
428
429    _XcmsCopyISOLatin1Lowered(name_lowered, tmpName);
430
431    /*
432     * Now, remove spaces.
433     */
434    for (i = 0, j = 0; j < len; j++) {
435	if (!isspace(name_lowered[j])) {
436	    name_lowered[i++] = name_lowered[j];
437	}
438    }
439    name_lowered[i] = '\0';
440
441    left = 0;
442    right = nEntries - 1;
443    while (left <= right) {
444	i = (left + right) >> 1;
445	pair = &pairs[i];
446	j = strcmp(name_lowered, pair->first);
447	if (j < 0)
448	    right = i - 1;
449	else if (j > 0)
450	    left = i + 1;
451	else {
452	    break;
453	}
454    }
455    if (len > 63) Xfree(name_lowered);
456
457    if (left > right) {
458	if (retval == 2) {
459	    if (*name != tmpName) {
460		*name = tmpName;
461	    }
462	    return(_XCMS_NEWNAME);
463	}
464	return(XcmsFailure);
465    }
466
467    if (pair->flag == CYCLE) {
468	return(XcmsFailure);
469    }
470    if (pair->flag == VISITED) {
471	pair->flag = CYCLE;
472	return(XcmsFailure);
473    }
474
475    if (_XcmsParseColorString(ccc, pair->second, pColor) == XcmsSuccess) {
476	/* f2 contains a numerical string specification */
477	return(XcmsSuccess);
478    } else {
479	/* f2 does not contain a numerical string specification */
480	tmpName = pair->second;
481	pair->flag = VISITED;
482	retval = 2;
483	goto Retry;
484    }
485}
486
487
488/*
489 *	NAME
490 *		RemoveSpaces
491 *
492 *	SYNOPSIS
493 */
494static int
495RemoveSpaces(
496    char *pString)
497/*
498 *	DESCRIPTION
499 *		Removes spaces from string.
500 *
501 *	RETURNS
502 *		Void
503 *
504 */
505{
506    int i, count = 0;
507    char *cptr;
508
509    /* REMOVE SPACES */
510    cptr = pString;
511    for (i = (int)strlen(pString); i; i--, cptr++) {
512	if (!isspace(*cptr)) {
513	    *pString++ = *cptr;
514	    count++;
515	}
516    }
517    *pString = '\0';
518    return(count);
519}
520
521
522/*
523 *	NAME
524 *		stringSectionSize - determine memory needed for strings
525 *
526 *	SYNOPSIS
527 */
528static int
529stringSectionSize(
530    FILE *stream,
531    int	*pNumEntries,
532    int	*pSectionSize)
533/*
534 *	DESCRIPTION
535 *		Determines the amount of memory required to store the
536 *		color name strings and also the number of strings.
537 *
538 *	RETURNS
539 *		XcmsSuccess if succeeded, otherwise XcmsFailure.
540 *
541 */
542{
543    char buf[XCMSDB_MAXLINELEN];
544    char token[XCMSDB_MAXLINELEN];
545    char token2[XCMSDB_MAXLINELEN];
546    char *pBuf;
547    char *f1;
548    char *f2;
549    size_t i;
550
551    unsigned int numEntries = 0;
552    unsigned int sectionSize = 0;
553
554    *pNumEntries = 0;
555    *pSectionSize = 0;
556
557    /*
558     * Advance to START_TOKEN
559     *	 Anything before is just considered as comments.
560     */
561
562    while((pBuf = fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
563	if ((sscanf(buf, "%s %s", token, token2))
564		&& (strcmp(token, START_TOKEN) == 0)) {
565	    if (strcmp(token2, FORMAT_VERSION) != 0) {
566		/* text file not in the right format */
567		return(XcmsFailure);
568	    }
569	    break;
570	} /* else it was just a blank line or comment */
571    }
572
573    if (pBuf == NULL) {
574	return(XcmsFailure);
575    }
576
577    while((fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
578	if ((sscanf(buf, "%s", token)) && (strcmp(token, END_TOKEN) == 0)) {
579	    break;
580	}
581
582	if (field2(buf, DELIM_CHAR, &f1, &f2) != XcmsSuccess) {
583	    return(XcmsFailure);
584	}
585
586	numEntries++;
587	if (numEntries >= INT_MAX)
588	    return(XcmsFailure);
589
590	i = strlen(f1);
591	if (i >= INT_MAX - sectionSize)
592	    return(XcmsFailure);
593	sectionSize += i + 1;
594	for (; i; i--, f1++) {
595	    /* REMOVE SPACES FROM COUNT */
596	    if (isspace(*f1)) {
597		sectionSize--;
598	    }
599	}
600
601	i = strlen(f2);
602	if (i >= INT_MAX - sectionSize)
603	    return(XcmsFailure);
604	sectionSize += i + 1;
605	for (; i; i--, f2++) {
606	    /* REMOVE SPACES FROM COUNT */
607	    if (isspace(*f2)) {
608		sectionSize--;
609	    }
610	}
611
612    }
613
614    *pNumEntries = (int) numEntries;
615    *pSectionSize = (int) sectionSize;
616
617    return(XcmsSuccess);
618}
619
620
621/*
622 *	NAME
623 *		ReadColornameDB - Read the Color Name Database
624 *
625 *	SYNOPSIS
626 */
627static Status
628ReadColornameDB(
629    FILE *stream,
630    XcmsPair *pRec,
631    char *pString)
632/*
633 *	DESCRIPTION
634 *		Loads the Color Name Database from a text file.
635 *
636 *	RETURNS
637 *		XcmsSuccess if succeeded, otherwise XcmsFailure.
638 *
639 */
640{
641    char buf[XCMSDB_MAXLINELEN];
642    char token[XCMSDB_MAXLINELEN];
643    char token2[XCMSDB_MAXLINELEN];
644    char *f1;
645    char *f2;
646    char *pBuf;
647
648    /*
649     * Advance to START_TOKEN
650     *	 Anything before is just considered as comments.
651     */
652
653    while((pBuf = fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
654	if ((sscanf(buf, "%s %s", token, token2))
655		&& (strcmp(token, START_TOKEN) == 0)) {
656	    if (strcmp(token2, FORMAT_VERSION) != 0) {
657		/* text file not in the right format */
658		return(XcmsFailure);
659	    }
660	    break;
661	} /* else it was just a blank line or comment */
662    }
663
664    if (pBuf == NULL) {
665	return(XcmsFailure);
666    }
667
668    /*
669     * Process lines between START_TOKEN to END_TOKEN
670     */
671
672    while ((fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
673	if ((sscanf(buf, "%s", token)) && (strcmp(token, END_TOKEN) == 0)) {
674	    /*
675	     * Found END_TOKEN so break out of for loop
676	     */
677	    break;
678	}
679
680	/*
681	 * Get pairs
682	 */
683	if (field2(buf, DELIM_CHAR, &f1, &f2) != XcmsSuccess) {
684	    /* Invalid line */
685	    continue;
686	}
687
688	/*
689	 * Add strings
690	 */
691
692	/* Left String */
693	pRec->first = pString;
694	_XcmsCopyISOLatin1Lowered(pString, f1);
695	pString += (1 + RemoveSpaces(pString));
696	pRec->second = pString;
697	/* Right String */
698	_XcmsCopyISOLatin1Lowered(pString, f2);
699	pString += RemoveSpaces(pString) + 1;
700	pRec++;
701
702    }
703
704    return(XcmsSuccess);
705}
706
707
708/*
709 *	NAME
710 *		LoadColornameDB - Load the Color Name Database
711 *
712 *	SYNOPSIS
713 */
714static Status
715LoadColornameDB(void)
716/*
717 *	DESCRIPTION
718 *		Loads the Color Name Database from a text file.
719 *
720 *	RETURNS
721 *		XcmsSuccess if succeeded, otherwise XcmsFailure.
722 *
723 */
724{
725    int size;
726    FILE *stream;
727    const char *pathname;
728    struct stat txt;
729    int length;
730
731    /* use and name of this env var is not part of the standard */
732    /* implementation-dependent feature */
733    if ((pathname = getenv("XCMSDB")) == NULL) {
734	pathname = XCMSDB;
735    }
736
737    length = (int)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    if (strings == NULL || pairs == NULL) {
766	free(strings);
767	free(pairs);
768	(void) fclose(stream);
769	XcmsColorDbState = XcmsDbInitFailure;
770	return(XcmsFailure);
771    }
772
773    ReadColornameDB(stream, pairs, strings);
774    (void) fclose(stream);
775
776    /*
777     * sort the pair recs
778     */
779    qsort((char *)pairs, nEntries, sizeof(XcmsPair), FirstCmp);
780
781    XcmsColorDbState = XcmsDbInitSuccess;
782    return(XcmsSuccess);
783}
784
785
786/************************************************************************
787 *									*
788 *			API PRIVATE ROUTINES				*
789 *									*
790 ************************************************************************/
791
792/*
793 *	NAME
794 *		_XcmsCopyISOLatin1Lowered
795 *
796 *	SYNOPSIS
797 */
798void
799_XcmsCopyISOLatin1Lowered(
800    char *dst,
801    const char *src)
802/*
803 *	DESCRIPTION
804 *		ISO Latin-1 case conversion routine
805 *		Identical to XmuCopyISOLatin1Lowered() but provided here
806 *		to eliminate need to link with libXmu.a.
807 *
808 *		IMPLEMENTORS NOTE:
809 *		    This routine is also used in XcmsFormatOfPrefix.
810 *
811 *	RETURNS
812 *		Void
813 *
814 */
815{
816    register unsigned char *dest;
817    register const unsigned char *source;
818
819    for (dest = (unsigned char *)dst, source = (const unsigned char *)src;
820	 *source;
821	 source++, dest++)
822    {
823	if ((*source >= XK_A) && (*source <= XK_Z))
824	    *dest = *source + (XK_a - XK_A);
825	else if ((*source >= XK_Agrave) && (*source <= XK_Odiaeresis))
826	    *dest = *source + (XK_agrave - XK_Agrave);
827	else if ((*source >= XK_Ooblique) && (*source <= XK_Thorn))
828	    *dest = *source + (XK_oslash - XK_Ooblique);
829	else
830	    *dest = *source;
831    }
832    *dest = '\0';
833}
834
835
836/*
837 *	NAME
838 *		_XcmsResolveColorString -
839 *
840 *	SYNOPSIS
841 */
842Status
843_XcmsResolveColorString (
844    XcmsCCC ccc,
845    const char **color_string,
846    XcmsColor *pColor_exact_return,
847    XcmsColorFormat result_format)
848/*
849 *	DESCRIPTION
850 *		The XcmsLookupColor function finds the color specification
851 *		associated with a color name in the Device-Independent Color
852 *		Name Database.
853 *	RETURNS
854 *		XcmsFailure if failed to convert valid color string.
855 *		XcmsSuccess if succeeded in converting color string to
856 *			XcmsColor.
857 *		_XCMS_NEWNAME if failed to parse the string or find it in
858 *			the database, or if succeeded in looking it up and
859 *			found another name which is not in the database.
860 *			Note that the new name is returned in color_string.
861 *
862 *		This function returns both the color specification found in the
863 *		database (db specification) and the color specification for the
864 *		color displayable by the specified screen (screen
865 *		specification).  The calling routine sets the format for these
866 *		returned specifications in the XcmsColor format component.
867 *		If XcmsUndefinedFormat, the specification is returned in the
868 *		format used to store the color in the database.
869 */
870{
871    XcmsColor dbWhitePt;	/* whitePt associated with pColor_exact_return*/
872				/*    the screen's white point */
873    XcmsColor *pClientWhitePt;
874    int retval;
875    const char *strptr = whitePtStr;
876
877/*
878 * 0. Check for invalid arguments.
879 */
880    if (ccc == NULL || (*color_string)[0] == '\0' || pColor_exact_return == NULL) {
881	return(XcmsFailure);
882    }
883
884/*
885 * 1. First attempt to parse the string
886 *    If successful, then convert the specification to the target format
887 *    and return.
888 */
889    if (_XcmsParseColorString(ccc, *color_string, pColor_exact_return)
890	    == 1) {
891	if (result_format != XcmsUndefinedFormat
892		&& pColor_exact_return->format != result_format) {
893	    /* need to be converted to the target format */
894	    return(XcmsConvertColors(ccc, pColor_exact_return, 1,
895		    result_format, (Bool *)NULL));
896	} else {
897	    return(XcmsSuccess);
898	}
899    }
900
901/*
902 * 2. Attempt to find it in the DI Color Name Database
903 */
904
905    /*
906     * a. Convert String into a XcmsColor structure
907     *       Attempt to extract the specification for color_string from the
908     *       DI Database (pColor_exact_return).  If the DI Database does not
909     *	     have this entry, then return failure.
910     */
911    retval = _XcmsLookupColorName(ccc, color_string, pColor_exact_return);
912
913    if (retval != XcmsSuccess) {
914	/* color_string replaced with a color name, or not found */
915	return(_XCMS_NEWNAME);
916    }
917
918    if (pColor_exact_return->format == XcmsUndefinedFormat) {
919	return(XcmsFailure);
920    }
921
922    /*
923     * b. If result_format not defined, then assume target format
924     *	  is the exact format.
925     */
926    if (result_format == XcmsUndefinedFormat) {
927	result_format = pColor_exact_return->format;
928    }
929
930    if ((ClientWhitePointOfCCC(ccc))->format == XcmsUndefinedFormat) {
931	pClientWhitePt = ScreenWhitePointOfCCC(ccc);
932    } else {
933	pClientWhitePt = ClientWhitePointOfCCC(ccc);
934    }
935
936    /*
937     * c. Convert to the target format, making adjustments for white
938     *	  point differences as necessary.
939     */
940    if (XCMS_DD_ID(pColor_exact_return->format)) {
941	/*
942	 * The spec format is Device-Dependent, therefore assume the
943	 *    its white point is the Screen White Point.
944	 */
945	if (XCMS_DD_ID(result_format)) {
946	    /*
947	     * Target format is Device-Dependent
948	     *	Therefore, DD --> DD conversion
949	     */
950	    return(_XcmsDDConvertColors(ccc, pColor_exact_return,
951		    1, result_format, (Bool *) NULL));
952	} else {
953	    /*
954	     * Target format is Device-Independent
955	     *	Therefore, DD --> DI conversion
956	     */
957	    if (ccc->whitePtAdjProc && !_XcmsEqualWhitePts(ccc,
958		    pClientWhitePt, ScreenWhitePointOfCCC(ccc))) {
959		return((*ccc->whitePtAdjProc)(ccc, ScreenWhitePointOfCCC(ccc),
960			pClientWhitePt, result_format,
961			pColor_exact_return, 1, (Bool *) NULL));
962	    } else {
963		if (_XcmsDDConvertColors(ccc, pColor_exact_return, 1,
964			XcmsCIEXYZFormat, (Bool *) NULL) == XcmsFailure) {
965		    return(XcmsFailure);
966		}
967		return(_XcmsDIConvertColors(ccc, pColor_exact_return,
968			pClientWhitePt, 1, result_format));
969	    }
970	}
971    } else {
972	/*
973	 * The spec format is Device-Independent, therefore attempt
974	 * to find a database white point.
975	 *
976	 * If the Database does not have a white point, then assume the
977	 * database white point is the same as the Screen White Point.
978	 */
979
980	if (_XcmsLookupColorName(ccc, &strptr, &dbWhitePt) != 1) {
981	    memcpy((char *)&dbWhitePt,
982		   (char *)&ccc->pPerScrnInfo->screenWhitePt,
983		   sizeof(XcmsColor));
984	}
985	if (XCMS_DD_ID(result_format)) {
986	    /*
987	     * Target format is Device-Dependent
988	     *	Therefore, DI --> DD conversion
989	     */
990	    if (ccc->whitePtAdjProc && !_XcmsEqualWhitePts(ccc,
991		    &dbWhitePt, ScreenWhitePointOfCCC(ccc))) {
992		return((*ccc->whitePtAdjProc)(ccc, &dbWhitePt,
993			ScreenWhitePointOfCCC(ccc), result_format,
994			pColor_exact_return, 1, (Bool *)NULL));
995	    } else {
996		if (pColor_exact_return->format != XcmsCIEXYZFormat) {
997		    if (_XcmsDIConvertColors(ccc, pColor_exact_return,
998			    &dbWhitePt, 1, XcmsCIEXYZFormat) == XcmsFailure) {
999			return(XcmsFailure);
1000		    }
1001		}
1002		return (_XcmsDDConvertColors(ccc, pColor_exact_return, 1,
1003			result_format, (Bool *)NULL));
1004	    }
1005	} else {
1006	    /*
1007	     * Target format is Device-Independent
1008	     *	Therefore, DI --> DI conversion
1009	     */
1010	    if (ccc->whitePtAdjProc && !_XcmsEqualWhitePts(ccc,
1011		    &dbWhitePt, pClientWhitePt)) {
1012		/*
1013		 * The calling routine wants to resolve this color
1014		 * in terms if it's white point (i.e. Client White Point).
1015		 * Therefore, apply white adjustment for the displacement
1016		 * between dbWhitePt to clientWhitePt.
1017		 */
1018		return((*ccc->whitePtAdjProc)(ccc, &dbWhitePt,
1019			pClientWhitePt, result_format,
1020			pColor_exact_return, 1, (Bool *)NULL));
1021	    } else if (_XcmsEqualWhitePts(ccc,
1022		    &dbWhitePt, pClientWhitePt)) {
1023		/*
1024		 * Can use either dbWhitePt or pClientWhitePt to
1025		 * convert to the result_format.
1026		 */
1027		if (pColor_exact_return->format == result_format) {
1028		    return(XcmsSuccess);
1029		} else {
1030		    return (_XcmsDIConvertColors(ccc, pColor_exact_return,
1031			    &dbWhitePt, 1, result_format));
1032		}
1033	    } else {
1034		/*
1035		 * Need to convert to a white point independent color
1036		 * space (let's choose CIEXYZ) then convert to the
1037		 * target color space.  Why? Lets assume that
1038		 * pColor_exact_return->format and result format
1039		 * are white point dependent format (e.g., CIELUV, CIELAB,
1040		 * TekHVC ... same or any combination). If so, we'll
1041		 * need to convert the color with dbWhitePt to an absolute
1042		 * spec (i.e.  non-white point dependent) then convert that
1043		 * absolute value with clientWhitePt to the result_format.
1044		 */
1045		if (pColor_exact_return->format != XcmsCIEXYZFormat) {
1046		    if (_XcmsDIConvertColors(ccc, pColor_exact_return,
1047			    &dbWhitePt, 1, XcmsCIEXYZFormat) == XcmsFailure) {
1048			return(XcmsFailure);
1049		    }
1050		}
1051		if (result_format == XcmsCIEXYZFormat) {
1052		    return(XcmsSuccess);
1053		} else {
1054		    return(_XcmsDIConvertColors(ccc, pColor_exact_return,
1055			    pClientWhitePt, 1, result_format));
1056		}
1057	    }
1058	}
1059    }
1060}
1061