Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtSvc / DtUtil2 / XlationSvc.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: XlationSvc.c /main/13 1999/10/14 15:59:35 mgreess $ */
24 /****************************************************************************
25 $FILEBEG$:    XlationSvc.c
26 $PROJECT$:    Cde 1.0
27 $COMPONENT$:  DtXlate service
28 $1LINER$:     Implements a translation service using tables and regex search
29 $COPYRIGHT$:
30  (c) Copyright 1993, 1994 Hewlett-Packard Company
31  (c) Copyright 1993, 1994 International Business Machines Corp.
32  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
33  (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of Novell, Inc.
34 $END$
35  ****************************************************************************
36  ************************************<+>*************************************/
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #if defined(sun)
42 #include <sys/utsname.h>
43 #endif
44 #include <limits.h>           /* INT_MAX */
45 #include <pwd.h>              /* for getpw... */
46 #include <sys/utsname.h>      /* for uname */
47 #include <sys/param.h>        /* MAXPATHLEN */
48
49 /* for RADIXCHAR and nl_langinfo */
50 #if defined(linux)
51 # define RADIXCHAR MON_DECIMAL_POINT
52 #endif
53 #include <langinfo.h>       
54
55 #if defined(sun) || defined(USL) || defined(__uxp__)
56 #include <regexpr.h>          /* for compile,advance */
57 #else
58 #include <regex.h>            /* for regcomp,regexec */
59 #endif
60
61 /* for Xrm */
62 #include <X11/Intrinsic.h>
63 #include <X11/Xresource.h>
64
65 /*=================================================================
66 $SHAREDBEG$:  This header appears in all appropriate DtXlate topics
67 =======================================================$SKIP$======*/
68 /*$INCLUDE$*/ 
69 #include "XlationSvc.h"
70 #include "LocaleXlate.h"
71 /*$END$*/
72
73 /**** For extensive Introductory info, go to the end of this file ****/
74
75 /*========================================================*/
76 /*====================== Constants =======================*/
77 /*========================================================*/
78
79 /*=============== private =================*/
80 /*#define DBG_MATCHING  ** if defined, debugging matching stages are compiled */
81
82 #define DEBUG_XLATE_NAME        "dtXlate.debugDtXlate"
83 #define DEBUG_XLATE_CLASS       "DtXlate.DebugDtXlate"
84
85 #define EOS                '\0'
86
87 /* A "random" number used to ensure that the Db has been initalized */
88 #define INIT_OCCURRED      2329479
89 #define PATH_SEPARATOR     ':'
90 #define DIR_SLASH          '/'
91
92 #define LESSTHAN_STR     "<"
93 #define EQUALS_STR       "="
94 #define CONTAINS_STR     "~"
95 #define MORETHAN_STR     ">"
96 #define INHIBIT_STR      "0"
97
98 #define MATCHALL_CHAR      '?'
99 #define ESCAPE_CHAR        '\\'
100 #define COMMENT_CHAR       '!'
101 #define QUOTE_CHAR         '\"'
102 #define OPER_SEPARATOR     ','
103 #define STDVALUE_SEPARATOR '.'
104 #define MATCHALL_VER       0
105
106 #define PLATFORM_QUARK   0      /* index offsets into quarklist */
107 #define VERSION_QUARK    1
108 #define OPERS_QUARK      2
109 #define DIRECTION_QUARK  3
110 #define FIRSTSTD_QUARK   4
111
112 #define MAXSPECQUARKS    43     /* std + platform + version + operation */
113 #define MAXSTDQUARKS     40
114 #define MAXRHSSIZE       100    /* max supported RHS size */
115 #define MAXLHSSIZE       200    /* max supported LHS size */
116 #define MAXINTSTRSIZE    15     /* handle any long int -> string */
117
118 typedef enum {
119    __DtXLATE_TYPE_NONE = 0,
120    __DtXLATE_TYPE_INHIBIT = 1,
121    __DtXLATE_TYPE_REGEX = 2,
122    __DtXLATE_TYPE_PURE = 3,
123    __DtXLATE_TYPE_CONTAINS = 4
124 } __DtXlateType;
125
126 #if defined(sun) || defined(USL) || defined(__uxp__)
127 /* Sun doesn't support regcomp() and regexec() yet, so
128    define this here and fill it will the results of advance() */
129 typedef struct regmatch_t {
130     int rm_so;        /* start offset */
131     int rm_eo;        /* end offset */
132 } regmatch_t;
133 #endif
134
135 /*========================================================*/
136 /*====================== Typedefs ========================*/
137 /*========================================================*/
138
139 #if DOC
140 /*========================================================*/
141 $PTYPEBEG$: __DtXlateDbRec
142 $1LINER$:  A private object used to represent translation dbs
143 $SUMMARY$:
144 __DtXlateDbRec is the type of the contents of a translation database
145 object.  The database object must be opened before use and closed
146 after use.  The definition of the object is opaque to users.
147 $ARGS$:
148 xrmDb:         Xrm database used to hold specs
149 initGuard:     used to test whether Db initialized
150 /*================================================$SKIP$==*/
151 #endif
152
153 /*$DEF$*/
154 typedef struct __DtXlateDbRec
155 {
156   XrmDatabase  xrmDb;
157   int          initGuard;
158   Boolean      debugMode;
159 } __DtXlateDbRec;
160 /*$END$*/
161
162 #if DOC
163 /*========================================================*/
164 $PTYPEBEG$: __DtXlateSrchData
165 $1LINER$:  A private object used to collect search-related data
166 $SUMMARY$:
167 __DtXlateSrchData stores all the data pertaining to a search
168 and the search results.  The search routines utilize this
169 to maintain status info over multiple calls by the
170 enumeration routines and to return info to the routine
171 that initiated the search.
172
173 $ARGS$:
174 filterQuarks:  quark list for the platform, version, and op filter
175 stdValueQuarks:   quark list for the std value
176 opValue:       ptr to operation-specific value string
177 SpecRef:       indices 1 to MAXSPECQUARKS point to matching Xrm strings
178 SubEx:         indices 1 to MAXSPECQUARKS index sub exprs in opValue
179 /*================================================$SKIP$==*/
180 #endif
181 /*$DEF$*/
182 typedef struct __DtXlateSrchData
183 {
184    /* db info */
185    _DtXlateDb      db;
186
187    /* query info */
188    const char *    platformStr;
189    XrmQuark        platformQuark;
190    int             version;
191    char            verStr[MAXINTSTRSIZE];   /* handle any long int */
192    const char *    operStr;
193    int             operStrLen;
194    XrmQuark        operQuark;
195    XrmQuark        lessThanQuark;
196    XrmQuark        equalsToQuark;
197    XrmQuark        containsQuark;
198    XrmQuark        moreThanQuark;
199    XrmQuark        inhibitQuark;
200
201    /* query or search info */
202    XrmQuark        stdValueQuarks[MAXSTDQUARKS];
203    const char *    opValue;
204
205    /* search info */
206    __DtXlateType   curTransType;
207    __DtXlateType   bestTransType;
208    int             curScore;
209    int             bestScore;
210    /* MAXSPECQUARKS is depended upon elsewhere to be the size of these */
211    const char *    curSpecRefs[MAXSPECQUARKS];
212    const char *    bestSpecRefs[MAXSPECQUARKS];  
213    regmatch_t      curSubEx[MAXSPECQUARKS];   /* pattern match data */
214    regmatch_t      bestSubEx[MAXSPECQUARKS];  /* pattern match data */
215 } __DtXlateSrchData;
216 /*$END$*/
217
218 /*========================================================*/
219 /*================== Private routines ====================*/
220 /*========================================================*/
221
222 #if DOC
223 /*========================================================*/
224 $PFUNBEG$:  ExpandPath()
225 $1LINER$: adds current working directory to front of path if its relative
226 $SUMMARY$: 
227 If path is absolute, returns a malloced copy.
228 If path is relative, inserts the CWD in front of the relative path
229 and returns a mallocedd memory.
230
231 The caller must free the memory when no longer needed.
232 $ARGS$:
233 filespec:  the pathname
234 $RETURNS$:
235 ptr to mallocedd memory or NULL
236 /*================================================$SKIP$==*/
237 #endif
238
239 char * ExpandPath (
240        const char * filespec)
241 {  /* $CODE$ */
242     char tmpPath[MAXPATHLEN + 2];
243     char * pathName;
244     char * eos = NULL;
245     const char * slash = NULL;
246
247     /* test args */
248     if (NULL == filespec) return NULL;
249
250     /*** is the file absolute ***/
251     /* if filespec begins with / then it is absolute */
252     if (   (   MB_CUR_MAX == 1 
253             || mblen(filespec, MB_CUR_MAX) == 1)/* 1st char is 1 byte */
254          && *filespec == DIR_SLASH)  /* and its a / */
255     {
256        return strdup(filespec);        /* RETURN */
257     }
258
259     /*** otherwise, make it relative to the current directory ***/
260
261     /* get user's current working directory */
262     if (getcwd(tmpPath, MAXPATHLEN) == NULL) return NULL; /* RETURN: error */
263
264     /*** add a slash to end of path component, if needed ***/
265     /* get end of the string */
266     eos = tmpPath + strlen(tmpPath);
267     /* get last slash */
268     _DtMBStrrchr(tmpPath,DIR_SLASH,-1,&slash);
269     if (slash != (eos-1)) /* is slash last char of path? */
270     {
271        *eos++ = DIR_SLASH; 
272        *eos = EOS; 
273     }
274
275     /* make a malloc'd copy of the path with room to grow */
276     pathName = malloc(sizeof(char) * 
277                  (strlen(filespec) + (eos-tmpPath) + 5) ); /* 5: arbitrary */
278     if (NULL == pathName) return NULL;          /* RETURN: error */
279
280     /* build the absolute path */
281     strcpy(pathName,tmpPath);
282     strcat(pathName,filespec);
283
284     return pathName;                             /* RETURN: found */
285 }  /* $END$ */
286
287 #if DOC
288 /*========================================================*/
289 $PFUNBEG$:  DeleteDbMem()
290 $1LINER$: Zeros out the db mem & frees it
291 $SUMMARY$: 
292 Zeros out the db mem & frees it
293 The xrmDb should have already been destroyed before calling
294 this function.
295 $ARGS$:
296 io_db:  db to delete
297 $RETURNS$:
298 /*================================================$SKIP$==*/
299 #endif
300
301 static
302 void DeleteDbMem(
303       _DtXlateDb * io_db)
304 { /*$CODE$*/
305        /* zero out object mem and free it */
306        (*io_db)->xrmDb = NULL;
307        (*io_db)->initGuard = 0;
308        free(*io_db);
309        *io_db= NULL;
310 } /*$END$*/
311
312 #if DOC
313 /*========================================================*/
314 $IFUNBEG$:  _DtMBStrchr()
315 $1LINER$: Searches for a character in a multibyte string
316 $SUMMARY$: 
317 Returns in 'ret_ptr' the address of the first occurence of 'value'
318 in string s1.  Value may also be the end of string marker '\0'.
319 $ARGS$:
320 $RETURNS$:
321 -1  If found an invalid character.
322  0  If found value in string s2
323  1  If found the null byte character without finding 'value'.
324     'ret_ptr' will also be null in this case.
325 /*================================================$SKIP$==*/
326 #endif
327
328 int _DtMBStrchr (
329     const char * s1,
330     int          value,
331     int          max_len,
332     const char * * ret_ptr )
333 {       /*$CODE$*/
334     int          len;
335     const char * p1;
336     wchar_t      wcs;
337
338     *ret_ptr = NULL;
339
340     if (!s1 || *s1 == '\0')
341         return 1;
342
343     if (max_len == 1)
344       {
345         *ret_ptr = strchr (s1, value);
346         if (*ret_ptr)
347             return 0;
348         return 1;
349       }
350
351     p1 = s1;
352     while (*p1 != '\0')
353       {
354         len = mblen (p1, max_len);
355         if (len == -1)
356             return -1;
357         if (len == 1)
358           {
359             if (*p1 == value)
360               {
361                 *ret_ptr = p1;
362                 return 0;
363               }
364             p1++;
365           }
366         else
367           {
368             if (mbstowcs (&wcs, p1, 1) == value)
369               {
370                 *ret_ptr = p1;
371                 return 0;
372               }
373             p1 += len;
374           }
375       }
376
377     /* check for match on EOS */
378     if (*p1 == value) *ret_ptr = p1;
379
380     return ((*ret_ptr) ? 0 : 1);
381 }       /*$CODE$*/
382
383
384 #if DOC
385 /*========================================================*/
386 $IFUNBEG$:  _DtMBStrrchr()
387 $1LINER$: Searches for a character in a multibyte string
388 $SUMMARY$: 
389 Returns in 'ret_ptr' the address of the last occurence of 'value'
390 in string s1.  Value may also be the end of string marker '\0'.
391 $ARGS$:
392 $RETURNS$:
393 -1  If found an invalid character.
394  0  If found value in string s2
395  1  If found the null byte character without finding 'value'.
396     'ret_ptr' will also be null in this case.
397 /*================================================$SKIP$==*/
398 #endif
399
400 int _DtMBStrrchr (
401     const char *   s1,
402     int            value,
403     int            max_len,
404     const char * * ret_ptr )
405 {        /*$CODE$*/
406     int          len;
407     const char * p1;
408     wchar_t      wcs;
409
410     *ret_ptr = NULL;
411
412     if (!s1 || *s1 == '\0')
413         return 1;
414
415     if (max_len == 1)
416       {
417         *ret_ptr = strrchr (s1, value);
418         if (*ret_ptr)
419             return 0;
420         return 1;
421       }
422
423     p1 = s1;
424     while (*p1 != '\0')
425       {
426         len = mblen (p1, max_len);
427         if (len == -1)
428             return -1;
429         if (len == 1)
430           {
431             if (*p1 == value)   *ret_ptr = p1;
432             p1++;
433           }
434         else
435           {
436             if (mbstowcs (&wcs, p1, 1) == value)   *ret_ptr = p1;
437             p1 += len;
438           }
439       }
440
441     /* check for match on EOS */
442     if (*p1 == value) *ret_ptr = p1;
443
444     return ((*ret_ptr) ? 0 : 1);
445 }
446
447
448 #if DOC
449 /*========================================================*/
450 $PFUNBEG$: SetDebugModeState()
451 $1LINER$:  Checks db for debug mode and sets flag
452 $SUMMARY$: 
453 $WARNING$:
454 $ARGS$:
455 $RETURNS$:
456 /*================================================$SKIP$==*/
457 #endif
458
459 static
460 void SetDebugModeState(
461        _DtXlateDb    dbRec)
462 {   /*$CODE$*/
463     XrmValue value;
464     char * str_type;
465     if (XrmGetResource(dbRec->xrmDb,
466            DEBUG_XLATE_NAME,DEBUG_XLATE_CLASS,&str_type,&value) == True)
467        dbRec->debugMode = True;
468 }
469
470
471 #if DOC
472 /*========================================================*/
473 $PFUNBEG$: ReplaceMatchallSubex()
474 $1LINER$:  Replace matchall subexpression refs (e.g. ?1) with values
475 $SUMMARY$: 
476 If subexpressions are specified and referenced, the
477 routine assumes that the string pointed to by pStr was
478 allocated using malloc() and can be resized using realloc().
479 The value and size of pStr may be different after the call.
480 $WARNING$:
481 This routine assumes it is working on a stdvalue expression
482 (e.g. from the LHS of a spec), that uses only stdvalue strings
483 or matchall-style subexpression replacement specs, e.g. ?1.
484 $ARGS$:
485 $RETURNS$:
486 /*================================================$SKIP$==*/
487 #endif
488
489 static
490 void ReplaceMatchallSubex(
491          char * *       pStr,
492          regmatch_t *   subex,
493          const char *   matchedStr)
494 {        /*$CODE$*/
495     char * nxt = *pStr;
496     char * old = *pStr;
497
498     /* strip escapes out */
499     for ( *nxt = *old;        /* xfer but don't advance */
500           *old != EOS;
501           *nxt = *old )       /* xfer but don't advance */
502     {
503         if ( *old == MATCHALL_CHAR )    /* if an escaped char follows */
504         {
505            /* if MATCHALL_CHAR is not followed by a digit, e.g. \1
506               or no replacement values exist, ignore it */
507            if (   NULL == subex 
508                || isdigit(*(old+1)) == 0 ) 
509            {
510               old++;                 /* go past the escape char */
511               *nxt++ = *old++;       /* keep just the char that was escaped
512                                         and assign here to avoid tranlating
513                                         that character, then move on to the 
514                                         next one */
515               continue;                      /* CONTINUE */
516            }
517            else  /* a value reference is being made */
518            {  /* get the refNum and advance the ptr */
519               int refNum, numLen;
520               int newOff, oldOff;
521
522               sscanf(++old,"%d%n", &refNum, &numLen);
523               old += numLen;   /* move old ptr past the ref number */
524
525 /* printf("%d=%s\n", refNum, &matchedStr[subex[refNum].rm_so]); ** DBG */
526
527               /* test for valid replacement */
528               if (   refNum >= 0 
529                   && refNum < MAXSPECQUARKS 
530                   && subex[refNum].rm_so != -1)
531               {
532                  int    repLen;
533                  int    strLen;
534                  char * oldTmp;
535                  char * newTmp;
536
537                  newOff = nxt - *pStr;
538                  oldOff = old - *pStr;
539                  repLen = subex[refNum].rm_eo - subex[refNum].rm_so;
540                  strLen = strlen(*pStr);
541
542                  /* expand memory and reset pointers */
543                  *pStr = realloc(*pStr,strLen+repLen+1);
544                  if (NULL == *pStr) return;           /* RETURN */
545                  nxt = *pStr + newOff;
546                  old = *pStr + oldOff;
547
548                  /* move rest back to leave room for the replacement value */
549                  oldTmp = *pStr+strLen;      /* pts to old EOS */
550                  newTmp = *pStr+strLen-(oldOff-newOff)+repLen;/*pts to new EOS*/
551                  while (oldTmp >= old) *newTmp-- = *oldTmp--; 
552
553                  /* replace the ref with a value but don't append EOS */
554                  strncpy(nxt,&matchedStr[subex[refNum].rm_so],repLen);
555                  nxt += repLen;  /* move new to end of replace string */
556                  old += repLen - (oldOff-newOff);  
557                                  /* move old to end of expanded old string */
558               }  /* if valid replacement */
559            }  /* if a replacement requested */
560            continue;                              /* CONTINUE */
561         }  /* if an escaped character */
562         /* if survived all the checks, can advance to next char */
563         nxt++;
564         old++;
565     }
566 }        /*$END$*/
567
568
569 #if DOC
570 /*========================================================*/
571 $PFUNBEG$: StripMetaAndReplaceEscSubex()
572 $1LINER$:  Strips off meta chars and replaces escaped subex (e.g. \1) values
573 $SUMMARY$:
574 Strip is performed in place if replaceValues is NULL.
575
576 If replaceValues are specified and referenced, the
577 routine assumes that the string pointed to by pStr was
578 allocated using malloc() and can be resized using realloc().
579 The value and size of pStr may be different after the call.
580
581 $WARNING$:
582 This routine assumes it is working on a value expression
583 (e.g. from the RHS of a spec), that uses meta chars and
584 regex(5)-style subexpression replacement specs.
585 $ARGS$:
586 $RETURNS$:
587 /*================================================$SKIP$==*/
588 #endif
589
590 static
591 void StripMetaAndReplaceEscSubex(
592          char * *       pStr,
593          const Boolean  keepEscChar,
594          const char * * replaceValues)
595 {        /*$CODE$*/
596     char * nxt = *pStr;
597     char * old = *pStr;
598     Boolean inQuote = False;
599
600     /* strip escapes out */
601     for ( *nxt = *old;        /* xfer but don't advance */
602           *old != EOS;
603           *nxt = *old )       /* xfer but don't advance */
604     {
605         if ( *old == ESCAPE_CHAR )    /* if an escaped char follows */
606         {
607            /* if ESCAPE_CHAR is not followed by a digit, e.g. \1
608               or no replacement values exist, ignore it */
609            if (   NULL == replaceValues 
610                || keepEscChar
611                || isdigit(*(old+1)) == 0 )
612            {
613               if (!keepEscChar) old++;       /* go past the escape char */
614               else *nxt++ = *old++;          /* copy esc char over */
615               *nxt++ = *old++;       /* keep just the char that was escaped
616                                         and assign here to avoid tranlating
617                                         that character, then move on to the
618                                         next one */
619               continue;                      /* CONTINUE */
620            }
621            else  /* a value reference is being made */
622            {  /* get the refNum and advance the ptr */
623               int refNum, numLen;
624               int newOff, oldOff;
625
626               sscanf(++old,"%d%n", &refNum, &numLen);
627               old += numLen;   /* move old ptr past the ref number */
628
629 /*  printf("%x=%s\n", replaceValues[refNum], replaceValues[refNum]); ** DBG */
630
631               /* test for valid replacement */
632               if (   refNum >= 0
633                   && refNum < MAXSPECQUARKS
634                   && replaceValues[refNum] != NULL)
635               {
636                  int    repLen;
637                  int    strLen;
638                  char * oldTmp;
639                  char * newTmp;
640
641                  newOff = nxt - *pStr;
642                  oldOff = old - *pStr;
643                  repLen = strlen(replaceValues[refNum]);
644                  strLen = strlen(*pStr);
645                  /* expand memory and reset pointers */
646                  *pStr = realloc(*pStr,strlen(*pStr)+repLen+1);
647                  if (NULL == *pStr) return;           /* RETURN */
648                  nxt = *pStr + newOff;
649                  old = *pStr + oldOff;
650
651                  /* move rest back to leave room for the replacement value */
652                  oldTmp = *pStr+strLen;      /* pts to old EOS */
653                  newTmp = *pStr+strLen-(oldOff-newOff)+repLen;/*pts to new EOS*/
654                  while (oldTmp >= old) *newTmp-- = *oldTmp--;
655
656                  /* replace the ref with a value but don't append EOS */
657                  strncpy(nxt,replaceValues[refNum],repLen);
658                  nxt += repLen;  /* move new to end of replace string */
659                  old += repLen - (oldOff-newOff);
660                                  /* move old to end of expanded old string */
661               }  /* if valid replacement */
662            }  /* if a replacement requested */
663            continue;                              /* CONTINUE */
664         }  /* if an escaped character */
665         else
666         {   /* a non-escaped char; make further checks */
667             if ( *old == COMMENT_CHAR )
668             {
669                *old = EOS;  /* end the string */
670                continue;                          /* CONTINUE */
671             }
672             else if ( *old == QUOTE_CHAR )
673             {
674                if ( !inQuote) inQuote = True;  /* start quote */
675                else inQuote = False;           /* end quote */
676                old++;     /* go to next char */
677                continue;                          /* CONTINUE */
678             }
679             else if ( !inQuote && isspace(*old) )
680             {
681                old++;     /* skip the space */
682                continue;                          /* CONTINUE */
683             }
684         }   /* else non-escaped char */
685
686         /* if survived all the checks, can advance to next char */
687         nxt++;
688         old++;
689     }
690 }        /*$END$*/
691
692 /*========================================================*/
693 /*============== Private Xlate routines ==================*/
694 /*========================================================*/
695
696
697 #if DOC
698 /*========================================================*/
699 $PFUNBEG$: PrintDbEntry()
700 $1LINER$:  Prints a db entry to stdout
701 $SUMMARY$: 
702 $ARGS$:
703 quarks:  NULLQUARK-terminated list of quarks for the LHS of the entry
704 value:   value of the RHS of the entry
705 $RETURNS$:
706 /*================================================$SKIP$==*/
707 #endif
708 static
709 void PrintDbEntry(
710        XrmQuarkList quarks,
711        XrmValue *   value)
712 {        /*$CODE$*/
713    char *   str;
714    XrmQuark quark;
715
716    /* print the entry */
717    quark=*quarks;
718    while ( quark != NULLQUARK )
719    {
720       str = XrmQuarkToString(quark);
721       fprintf(stderr,"%s", str);
722       quark = *(++quarks);
723       if (quark != NULLQUARK) fprintf(stderr,".");
724    }
725    fprintf(stderr,":%s\n",value->addr);
726 }        /*$END$*/
727
728
729 #if DOC
730 /*========================================================*/
731 $PFUNBEG$: strCaseiCmp
732 $1LINER$:  case insensitive string comparison
733 $SUMMARY$: 
734 Rolled my own because strcasecmp() not available on
735 all platforms.
736 $ARGS$:
737 $RETURNS$:
738 True:  strings match
739 False: they dont
740 /*================================================$SKIP$==*/
741 #endif
742
743 static
744 Boolean strCaseiCmp(
745        const char * str1,
746        const char * str2)
747 {        /*$CODE$*/
748    /* if ( !str1 || !str2 ) return False; *//* unneeded performance hit */
749    while ( *str1 && *str2 )
750      if ( tolower(*str1++) != tolower(*str2++) )  return False;
751    return (*str1 == *str2);
752 }        /*$END$*/
753
754 #if DOC
755 /*========================================================*/
756 $PFUNBEG$: CheckForMatchall()
757 $1LINER$:  Matches search pattern to spec data
758 $SUMMARY$: 
759 $ARGS$:
760 $RETURNS$:
761 /*================================================$SKIP$==*/
762 #endif
763
764 static
765 Boolean CheckForMatchall(
766            __DtXlateSrchData * srchData, 
767            const char *      matchallString,
768            const char *      matchingString)
769 {        /*$CODE$*/
770    int refNum = -1;
771    int score = 0;
772
773    /* test for a match all */
774    if (! (   matchallString[0] == MATCHALL_CHAR 
775           && (   matchallString[1] == EOS
776               || sscanf(matchallString+1,"%d", &refNum) == 1) ) )
777       return False;        /* RETURN: syntax error or non-matchall */
778
779    /* matchall occurred; save the matching string if valid ref num */
780    if (   refNum != -1 
781        && refNum >= 0 
782        && refNum < XtNumber(srchData->curSpecRefs) )
783    {
784       /* Don't store the string if it is just a matchall */
785       /* This allows a replacement ref to be deleted on a match 
786          for which there is no replacement value. */
787
788       if (   matchingString[0] == MATCHALL_CHAR
789           && matchingString[1] == EOS )
790       {
791          srchData->curSpecRefs[refNum] = NULL;
792          /* don't add to score for a matchall with no replacement value */
793       }
794       else
795       {
796          /* recall: string not owned by curSpecRefs */
797          srchData->curSpecRefs[refNum] = matchingString;
798          score = 1;
799       }
800    } 
801    else  /* not a value reference; just determine if a plain match */
802    {
803       if (   matchingString[0] == MATCHALL_CHAR
804           && matchingString[1] == EOS )
805           score = 1;      /* a plain matchall matches a plain matchall */
806    }
807
808    /* if a perfect matchall match, bump the score */
809    srchData->curScore += score;
810
811    return True;        /* RETURN: matchall */
812 }        /*$END$*/
813
814
815 #if DOC
816 /*========================================================*/
817 $PFUNBEG$: CheckSearchPlatformMatchesSpec()
818 $1LINER$:  Matches search pattern to spec data
819 $SUMMARY$: 
820 $ARGS$:
821 $RETURNS$:
822 /*================================================$SKIP$==*/
823 #endif
824
825 static
826 Boolean CheckSearchPlatformMatchesSpec(
827            __DtXlateSrchData * srchData, 
828            XrmQuark          specPlatformQuark)
829 {        /*$CODE$*/
830    /* CheckForMatchall stores away the matching string if of form ?n */
831    if (srchData->platformQuark != NULLQUARK)
832    {
833       char * specStr = XrmQuarkToString(specPlatformQuark);
834       /* CheckForMatchall incs score if appropriate */
835       if (CheckForMatchall(srchData,specStr,srchData->platformStr) == True)
836          return True;            /* RETURN: platform matches */
837    }
838    if (srchData->platformQuark == specPlatformQuark)
839    {
840       srchData->curScore += 2;   /* perfect match better than matchall match */
841       return True;               /* RETURN: platform matches */
842    }
843    return False;                 /* RETURN: platform doesnt match */
844 }        /*$END$*/
845
846
847 #if DOC
848 /*========================================================*/
849 $PFUNBEG$: CheckSearchVerMatchesSpec()
850 $1LINER$:  Matches search pattern to spec data
851 $SUMMARY$: 
852 $ARGS$:
853 $RETURNS$:
854 /*================================================$SKIP$==*/
855 #endif
856
857 static
858 Boolean CheckSearchVerMatchesSpec(
859            __DtXlateSrchData * srchData, 
860            XrmQuark          specVersionQuark)
861 {        /*$CODE$*/
862    const char * numStr = XrmQuarkToString(specVersionQuark);
863    int          lowerBnd = 0;
864    int          upperBnd = INT_MAX;
865    int          score = 0;
866
867    if (   srchData->version == MATCHALL_VER
868        || CheckForMatchall(srchData,numStr,srchData->verStr) == True )
869       return True;          /* RETURN; matchall specified */
870
871    /*** sscanf()-based parsing ***/
872    /* note that the (score=x) is an assignment, not a compare */
873    if (   (score=1) && sscanf(numStr,"%d-%d", &lowerBnd, &upperBnd) != 2
874        && (score=1) && sscanf(numStr,"%d+", &lowerBnd) != 1
875        && (score=2) && sscanf(numStr,"%d", &lowerBnd) != 1 )
876        return False;        /* RETURN: syntax error */
877
878    if ( lowerBnd > srchData->version || upperBnd < srchData->version )
879        return False;        /* RETURN: version doesnt match */
880
881    srchData->curScore += score;
882    return True;             /* RETURN: version matches */
883 }        /*$END$*/
884
885
886
887 #if DOC
888 /*========================================================*/
889 $PFUNBEG$: CheckSearchOperMatchesSpec()
890 $1LINER$:  Matches search pattern to spec data
891 $SUMMARY$: 
892 $ARGS$:
893 srchData:            state of the search
894 specOperationQuark:  quark for the operation specification string
895 $RETURNS$:
896 True:  if srchData->operation is found in specOperation string
897        or the specOperation string is a match all
898 False: if not
899 /*================================================$SKIP$==*/
900 #endif
901
902 static
903 Boolean CheckSearchOperMatchesSpec(
904            __DtXlateSrchData * srchData, 
905            XrmQuark          specOperationQuark)
906 {        /*$CODE$*/
907    const char * opStr = XrmQuarkToString(specOperationQuark);
908    int          hitLen;
909    const char * hit;
910    const char * remainingOps;
911
912    if (   srchData->operStr == NULL
913        || CheckForMatchall(srchData,opStr,srchData->operStr) == True
914        || CheckForMatchall(srchData,srchData->operStr,opStr) == True )
915       return True;          /* RETURN; matchall specified */
916
917    /* quark compare search */
918    if ( specOperationQuark == srchData->operQuark )
919        goto matched;
920
921    /*** strstr-based search ***/
922    hitLen = srchData->operStrLen;
923    remainingOps = opStr;
924    do 
925    {
926       /* look for operation in remainingOps */
927       hit = strstr(remainingOps,srchData->operStr);
928       /* see if the hit is on a complete token */
929       if (   NULL != hit
930           && (hit == remainingOps || *(hit-1) == OPER_SEPARATOR)
931           && (hit[hitLen] == EOS || hit[hitLen] == OPER_SEPARATOR) )
932       {
933 matched:
934          srchData->curScore += 2; /*perfect match better than a matchall match*/
935          return True;             /* RETURN: operation matches */
936       }
937    } while(hit != NULL && *(remainingOps = hit+1) != EOS);
938
939    return False;        /* RETURN: no match on operation */
940 }        /*$END$*/
941
942
943
944 #if DOC
945 /*========================================================*/
946 $PFUNBEG$: CheckSearchDirOpToStdMatchesSpec()
947 $1LINER$:  Matches search pattern to spec data
948 $SUMMARY$: 
949 $ARGS$:
950 $RETURNS$:
951 /*================================================$SKIP$==*/
952 #endif
953
954 static
955 Boolean CheckSearchDirOpToStdMatchesSpec(
956            __DtXlateSrchData * srchData, 
957            XrmQuark          specDirectionQuark)
958 {        /*$CODE$*/
959    int          score = 0;
960    __DtXlateType type = __DtXLATE_TYPE_NONE;
961
962    if (specDirectionQuark == srchData->inhibitQuark) 
963        return False;  /* RETURN: no match */
964
965    /* Note that the type and score expressions are assignments */
966    if (! (   (   (type=__DtXLATE_TYPE_REGEX) 
967               && (specDirectionQuark == srchData->lessThanQuark))
968           || (   (type=__DtXLATE_TYPE_PURE)
969               && (score=1) 
970               && (specDirectionQuark == srchData->equalsToQuark)) 
971           || (   (type=__DtXLATE_TYPE_CONTAINS)
972               && (score=1) 
973               && (specDirectionQuark == srchData->containsQuark)) ) )
974        return False;        /* RETURN: no match */
975
976    srchData->curScore += score;
977    srchData->curTransType = type;
978    return True;             /* RETURN: direction matches */
979 }        /*$END$*/
980
981
982 #if DOC
983 /*========================================================*/
984 $PFUNBEG$: CheckSearchDirStdToOpMatchesSpec()
985 $1LINER$:  Matches search pattern to spec data
986 $SUMMARY$: 
987 $ARGS$:
988 $RETURNS$:
989 /*================================================$SKIP$==*/
990 #endif
991
992 static
993 Boolean CheckSearchDirStdToOpMatchesSpec(
994            __DtXlateSrchData * srchData, 
995            XrmQuark          specDirectionQuark)
996 {        /*$CODE$*/
997    int          score = 0;
998    __DtXlateType type = __DtXLATE_TYPE_NONE;
999
1000    if (specDirectionQuark == srchData->inhibitQuark) 
1001        return False;  /* RETURN: no match */
1002
1003    /* Note that the type and score expressions are assignments */
1004    if (! (   (   (type=__DtXLATE_TYPE_REGEX) 
1005               && (specDirectionQuark == srchData->moreThanQuark))
1006           || (   (type=__DtXLATE_TYPE_PURE)
1007               && (score=1) 
1008               && (specDirectionQuark == srchData->equalsToQuark))
1009           || (   (type=__DtXLATE_TYPE_CONTAINS)
1010               && (score=1) 
1011               && (specDirectionQuark == srchData->containsQuark)) ) )
1012        return False;        /* RETURN: no match */
1013
1014    srchData->curScore += score;
1015    srchData->curTransType = type;
1016    return True;             /* RETURN: direction matches */
1017 }        /*$END$*/
1018
1019
1020
1021 #if DOC
1022 /*========================================================*/
1023 $PFUNBEG$: CheckSearchStdValueMatchesSpec()
1024 $1LINER$:  Matches search pattern to spec data
1025 $SUMMARY$: 
1026 $ARGS$:
1027 $RETURNS$:
1028 /*================================================$SKIP$==*/
1029 #endif
1030
1031 static
1032 Boolean CheckSearchStdValueMatchesSpec(
1033            __DtXlateSrchData * srchData, 
1034            XrmQuark *        specStdValueQuarks)
1035 {        /*$CODE$*/
1036    int        score = 0;
1037    int        unmatched = 0;
1038    XrmQuark * patQuarks;
1039
1040    /* walk through all available quarks */
1041    for ( patQuarks = srchData->stdValueQuarks;
1042          *specStdValueQuarks != NULLQUARK && *patQuarks != NULLQUARK;
1043          specStdValueQuarks++, patQuarks++ )
1044    { 
1045       char * specStr = XrmQuarkToString(*specStdValueQuarks);
1046       char * patStr = XrmQuarkToString(*patQuarks);
1047       if (    CheckForMatchall(srchData,specStr,patStr) == True 
1048            || CheckForMatchall(srchData,patStr,specStr) == True )
1049          continue;     /* no score for a matchall */
1050
1051       /* is not exact match, match fails */
1052       /* be case insensitive when comparing standard values */
1053       if (    *patQuarks != *specStdValueQuarks
1054            && strCaseiCmp(specStr,patStr) == False )
1055           return False;              /* RETURN: no match */
1056
1057       /* one more match--increase score, go to next */
1058       score++;
1059    }
1060
1061    /* find out how many stdValue fields were left unmatched */
1062    for ( unmatched = 0;
1063          *specStdValueQuarks != NULLQUARK;
1064          specStdValueQuarks++ ) 
1065        { unmatched++; }
1066
1067    /* Score is combo of the number matched - the number unmatched 
1068       and not counting the number matchalls that coincided with
1069       the search pattern.   This technique allows the spec for
1070       val1 to be at "better" match than the one for val2, and
1071       val2 to be a better match than val3, and val3 to be a better
1072       match than val4.
1073          .a.std     : val1          querypattern = a.std
1074          .?.std     : val2          querypattern = a.std
1075          .?1.std    : \\1val3       querypattern = a.std
1076          .a.std     : val4          querypattern = ?.std
1077          .?.std     : val5          querypattern = ?.std
1078          .?1.std    : \\1val6       querypattern = ?.std
1079
1080          .a.std.?   : val4          querypattern = a.std
1081          .?.std.?   : val5          querypattern = a.std
1082          .?.std.?1  : \\1val5       querypattern = a.std
1083          .?1.std.?  : \\1val5       querypattern = a.std
1084          .?1.std.?2 : \\1\\2val5    querypattern = a.std
1085          .a.std.?   : val4          querypattern = a.std.b
1086          .?.std.?   : val5          querypattern = a.std.b
1087          .?.std.?1  : \\1val5       querypattern = a.std.b
1088          .?1.std.?  : \\1val5       querypattern = a.std.b
1089          .?1.std.?2 : \\1\\2val5    querypattern = a.std.b
1090    */
1091    srchData->curScore += score + MAXSTDQUARKS - unmatched;
1092
1093    return True;             /* RETURN: direction matches */
1094 }        /*$END$*/
1095
1096
1097
1098 #if DOC
1099 /*========================================================*/
1100 $PFUNBEG$: CheckSearchOpValueMatchesSpec()
1101 $1LINER$:  Matches search pattern to spec data
1102 $SUMMARY$: 
1103 $ARGS$:
1104 $RETURNS$:
1105 /*================================================$SKIP$==*/
1106 #endif
1107
1108 static
1109 Boolean CheckSearchOpValueMatchesSpec(
1110            __DtXlateSrchData * srchData, 
1111            const char *        specOpValue)
1112 {        /*$CODE$*/
1113     char       opValBuf[MAXRHSSIZE]; /* max supported RHS size */
1114     char *     pOpValBuf = opValBuf;  /* need this for StripMeta... call */
1115     size_t     opValLen;
1116     Boolean    matches = False;
1117
1118     /* copy value to mutable memory */
1119     strncpy(opValBuf,specOpValue,sizeof(opValBuf));
1120     opValBuf[sizeof(opValBuf)-1] = EOS;
1121     opValLen = strlen(opValBuf);
1122
1123     /* depending on the translation type of the spec, do a 
1124        regexex match of the spec value pattern to the search 
1125        value or do a pure match */
1126     if (srchData->curTransType == __DtXLATE_TYPE_REGEX)
1127     {
1128 #if defined(sun) || defined(USL) || defined(__uxp__)
1129         char *     ex = NULL;
1130
1131         /* True: leave escape char in place */
1132         StripMetaAndReplaceEscSubex(&pOpValBuf,True,NULL);
1133
1134         /* we need to use regexex to pattern match */
1135         /* and we need to save of the reference value matches */
1136         if (   (ex = compile(opValBuf,NULL,NULL)) != NULL
1137             && advance(srchData->opValue,ex) != 0)
1138         {
1139             int matchSize;
1140             int subExCnt = nbra;  /* Sun global for advance() */
1141             matches = True;       /* if got this far */
1142
1143             /* need due to bug in advance()--operation doesn't meet documentation */
1144             if (NULL == loc1) loc1=(char *)srchData->opValue;
1145
1146             /* inc score by the size of the match after 
1147                scaling for the maximum possible match size */
1148             matchSize = loc2 - loc1; /*loc[12] are Sun globals for advance()*/
1149             if (matchSize < 0 || matchSize >= sizeof(opValBuf)) matchSize = 0;
1150
1151             /* NOTE: this scoring code should be identical in the 
1152                Sun-specific and non-Sun code blocks */
1153             if (matchSize == strlen(srchData->opValue))
1154             {
1155                /* if the matchSize is the length of srchData->opValue,
1156                   then we have a complete match.  In this case, use the
1157                   specificity of the pattern to pick the best match */
1158                /* NOTE: opValLen is a crude measure of specificity.  
1159                   A better measure would be to count the number of
1160                   literals/ranges that matched exactly.  When doing this,
1161                   a perfect match without regex syntax should rank higher
1162                   than a perfect match with regex syntax.  This is one
1163                   area where the current algorithm breaks.  For example:
1164                      opValue=23, pat1=23, pat2=[0-9]3.
1165                   Both patterns match and pat1 is a better match,
1166                   but not with the current length-based algorithm. */
1167                /* NOTE: this formula does not advance the score
1168                   to sizeof(opValBuf) for a perfect match.  Other match 
1169                   formulas use sizeof(opValBuf) as the max value
1170                   to indicate a perfect match. */
1171                srchData->curScore += matchSize + opValLen;
1172             }
1173             else
1174             {
1175                /* if its not a complete match, inc score by match size */
1176                srchData->curScore += matchSize;
1177             }
1178
1179             /* put sub expression matching stuff in srchData->curSubEx */
1180             for( ; nbra > 0; nbra-- )
1181             {
1182                 srchData->curSubEx[nbra].rm_so = braslist[nbra-1] - loc1;
1183                 srchData->curSubEx[nbra].rm_eo = braelist[nbra-1] - loc1;
1184             }
1185         }
1186         if (ex) free(ex);
1187 #else
1188         regex_t    re;
1189
1190         /* True: leave escape char in place */
1191         StripMetaAndReplaceEscSubex(&pOpValBuf,True,NULL);
1192
1193         /* we need to use regexex to pattern match */
1194         /* and we need to save of the reference value matches */
1195         if (   regcomp(&re,opValBuf,0) == 0
1196             && regexec(&re,srchData->opValue,
1197                    XtNumber(srchData->curSubEx),srchData->curSubEx,0) == 0)
1198         {
1199             int matchSize;
1200             matches = True;       /* if got this far */
1201
1202             /* inc score by the size of the match after 
1203                scaling for the maximum possible match size */
1204             matchSize = srchData->curSubEx[0].rm_eo - 
1205                                                srchData->curSubEx[0].rm_so;
1206
1207             /* NOTE: this scoring code should be identical in the 
1208                Sun-specific and non-Sun code blocks */
1209             if (matchSize == strlen(srchData->opValue))
1210             {
1211                /* if the matchSize is the length of srchData->opValue,
1212                   then we have a complete match.  In this case, use the
1213                   specificity of the pattern to pick the best match */
1214                /* NOTE: opValLen is a crude measure of specificity.  
1215                   A better measure would be to count the number of
1216                   literals/ranges that matched exactly.  When doing this,
1217                   a perfect match without regex syntax should rank higher
1218                   than a perfect match with regex syntax.  This is one
1219                   area where the current algorithm breaks.  For example:
1220                      opValue=23, pat1=23, pat2=[0-9]3.
1221                   Both patterns match and pat1 is a better match,
1222                   but not with the current length-based algorithm. */
1223                /* NOTE: this formula does not advance the score
1224                   to sizeof(opValBuf) for a perfect match.  Other match 
1225                   formulas use sizeof(opValBuf) as the max value
1226                   to indicate a perfect match. */
1227                srchData->curScore += matchSize + opValLen;
1228             }
1229             else
1230             {
1231                /* if its not a complete match, inc score by match size */
1232                srchData->curScore += matchSize;
1233             }
1234
1235             /* sub expression matching stuff already in srchData->curSubEx */
1236         }
1237         regfree(&re);
1238 #endif
1239     }
1240     else /* (srchData->curTransType == __DtXLATE_TYPE_PURE || __DtXLATE_TYPE_CONTAINS */
1241     {
1242         char * opValueInBuf;
1243
1244         /* False: strip escape char as well */
1245         StripMetaAndReplaceEscSubex(&pOpValBuf,False,NULL);
1246         matches = (strcmp(srchData->opValue,opValBuf) == 0);
1247
1248         /* if matches, inc score to show a perfect match (max poss value) */
1249         if (matches) srchData->curScore += sizeof(opValBuf);
1250
1251         /* don't test for contains if a perfect match or a pure match spec */
1252         if (matches || srchData->curTransType == __DtXLATE_TYPE_PURE)
1253            return matches;                            /* RETURN */
1254
1255         /*  (srchData->curTransType == __DtXLATE_TYPE_CONTAINS) */
1256         /* is opValue contained in opValBuf? */
1257         /* is opValBuf contained in opValue? */
1258         opValueInBuf = NULL;
1259         matches =    (opValBuf[0] != EOS && srchData->opValue[0] != EOS) 
1260                   && ((opValueInBuf=strstr(opValBuf,srchData->opValue)) != NULL
1261                       || strstr(srchData->opValue,opValBuf) != NULL);
1262
1263         /* if matches, inc score to show a contains match */
1264         if (matches) 
1265         {
1266            if (opValueInBuf) srchData->curScore += strlen(srchData->opValue);
1267            else srchData->curScore += strlen(opValBuf);
1268         }
1269     }
1270     return matches;
1271 }        /*$END$*/
1272
1273
1274
1275
1276 #if DOC
1277 /*========================================================*/
1278 $PFUNBEG$: FindStdToOpMatchCB()
1279 $1LINER$:  Matches std value of entry to search pattern; gets op value
1280 $SUMMARY$: 
1281 $ARGS$:
1282 $RETURNS$:
1283 /*================================================$SKIP$==*/
1284 #endif
1285
1286 static
1287 Bool FindStdToOpMatchCB(
1288             XrmDatabase *       database,
1289             XrmBindingList      bindings,
1290             XrmQuarkList        quarks,
1291             XrmRepresentation * type,
1292             XrmValue *          value,
1293             XPointer            client_data)
1294 {       /*$CODE$*/
1295    __DtXlateSrchData * srchData = (__DtXlateSrchData *) client_data;
1296
1297    /* always begin scoring from 0 and replacement values at NULL */
1298    srchData->curScore = 0;
1299    memset(srchData->curSpecRefs,0, sizeof(srchData->curSpecRefs));
1300
1301 #ifdef DBG_MATCHING
1302    fprintf(stderr,"FindStdToOpMatch: "); PrintDbEntry(quarks,value);
1303 #endif
1304
1305    /* check for a match */
1306    if ( CheckSearchPlatformMatchesSpec(srchData, 
1307                       quarks[PLATFORM_QUARK]) == False) 
1308       return False;      /* continue enumeration */
1309
1310 #ifdef DBG_MATCHING
1311    fprintf(stderr,"platform matches\n");
1312 #endif
1313
1314    if ( CheckSearchVerMatchesSpec(srchData, 
1315                       quarks[VERSION_QUARK]) == False) 
1316       return False;      /* continue enumeration */
1317
1318 #ifdef DBG_MATCHING
1319    fprintf(stderr,"ver matches\n");
1320 #endif
1321
1322    if ( CheckSearchOperMatchesSpec(srchData, 
1323                       quarks[OPERS_QUARK]) == False) 
1324       return False;      /* continue enumeration */
1325
1326 #ifdef DBG_MATCHING
1327    fprintf(stderr,"oper matches\n");
1328 #endif
1329
1330    if ( CheckSearchDirStdToOpMatchesSpec(srchData, 
1331                       quarks[DIRECTION_QUARK]) == False) 
1332       return False;      /* continue enumeration */
1333
1334 #ifdef DBG_MATCHING
1335    fprintf(stderr,"kind matches\n");
1336 #endif
1337
1338    /* now check for std value match and, if it is
1339       the best match so far, record the value */
1340    if ( CheckSearchStdValueMatchesSpec(srchData, 
1341                        &quarks[FIRSTSTD_QUARK]) == False )
1342    {
1343       if (srchData->db->debugMode)
1344       {
1345          fprintf(stderr,"mismatch ");
1346          PrintDbEntry(quarks,value);
1347       }
1348    
1349       return False;      /* continue enumeration */
1350    }
1351
1352 #ifdef DBG_MATCHING
1353    fprintf(stderr,"std value matches\n");
1354 #endif
1355
1356    if (srchData->db->debugMode)
1357    {
1358       fprintf(stderr,"match (%d) ",srchData->curScore);
1359       PrintDbEntry(quarks,value);
1360    }
1361    
1362    /* we have a match! (we made it through all match checks) */
1363    /* is it better than or same as any earlier match? */
1364    if ( srchData->curScore >= srchData->bestScore )
1365    {
1366       /* recall that all strings are owned by Xrm==>no need to free them */
1367       srchData->bestScore = srchData->curScore;
1368       srchData->bestTransType = srchData->curTransType;
1369       memcpy(srchData->bestSpecRefs,srchData->curSpecRefs,
1370                 sizeof(srchData->bestSpecRefs)); /* no array assignment in C */
1371       srchData->opValue = value->addr;
1372    }
1373
1374    return False;    /* continue enumeration */
1375 }       /*$END$*/
1376
1377
1378 #if DOC
1379 /*========================================================*/
1380 $PFUNBEG$: FindOpToStdMatchCB()
1381 $1LINER$:  Matches op value of entry to search pattern; gets std value
1382 $SUMMARY$: 
1383 $ARGS$:
1384 $RETURNS$:
1385 /*================================================$SKIP$==*/
1386 #endif
1387
1388 static
1389 Bool FindOpToStdMatchCB(
1390             XrmDatabase *       database,
1391             XrmBindingList      bindings,
1392             XrmQuarkList        quarks,
1393             XrmRepresentation * type,
1394             XrmValue *          value,
1395             XPointer            client_data)
1396 {       /*$CODE$*/
1397    __DtXlateSrchData * srchData = (__DtXlateSrchData *) client_data;
1398
1399    /* always begin scoring from 0 and subexpression indices at -1 */
1400    srchData->curScore = 0;
1401    memset(srchData->curSubEx,-1, sizeof(srchData->curSubEx));
1402
1403 #ifdef DBG_MATCHING
1404    fprintf(stderr,"FindOpToStdMatch: "); PrintDbEntry(quarks,value);
1405 #endif
1406
1407    /* check for a match */
1408    if ( CheckSearchPlatformMatchesSpec(srchData, 
1409                       quarks[PLATFORM_QUARK]) == False) 
1410       return False;      /* continue enumeration */
1411
1412 #ifdef DBG_MATCHING
1413    fprintf(stderr,"platform matches\n");
1414 #endif
1415
1416    if ( CheckSearchVerMatchesSpec(srchData, 
1417                       quarks[VERSION_QUARK]) == False) 
1418       return False;      /* continue enumeration */
1419
1420 #ifdef DBG_MATCHING
1421    fprintf(stderr,"ver matches\n");
1422 #endif
1423
1424    if ( CheckSearchOperMatchesSpec(srchData, 
1425                       quarks[OPERS_QUARK]) == False) 
1426       return False;      /* continue enumeration */
1427
1428 #ifdef DBG_MATCHING
1429    fprintf(stderr,"oper matches\n");
1430 #endif
1431
1432    if ( CheckSearchDirOpToStdMatchesSpec(srchData, 
1433                       quarks[DIRECTION_QUARK]) == False) 
1434       return False;      /* continue enumeration */
1435
1436 #ifdef DBG_MATCHING
1437    fprintf(stderr,"kind matches\n");
1438 #endif
1439
1440    /* now check for op value match and, if it is
1441       the best match so far, record the std value */
1442    if ( CheckSearchOpValueMatchesSpec(srchData, 
1443                                    value->addr) == False )
1444    {
1445       if (srchData->db->debugMode)
1446       {
1447          fprintf(stderr,"mismatch ");
1448          PrintDbEntry(quarks,value);
1449       }
1450    
1451       return False;      /* continue enumeration */
1452    }
1453
1454 #ifdef DBG_MATCHING
1455    fprintf(stderr,"op value matches\n");
1456 #endif
1457
1458    if (srchData->db->debugMode)
1459    {
1460       fprintf(stderr,"match (%d) ",srchData->curScore);
1461       PrintDbEntry(quarks,value);
1462    }
1463
1464    /* we have a match! (we made it through all match checks) */
1465    /* is it better than or same as any earlier match? */
1466    if ( srchData->curScore >= srchData->bestScore )
1467    {
1468       XrmQuarkList stdQ;
1469       XrmQuarkList curQ;
1470       /* recall that all strings are owned by Xrm==>no need to free them */
1471       srchData->bestScore = srchData->curScore;
1472       srchData->bestTransType = srchData->curTransType;
1473       memcpy(srchData->bestSubEx,srchData->curSubEx,
1474                 sizeof(srchData->bestSubEx)); /* no array assignment in C */
1475       /* store off the std value of the best match */
1476       stdQ = srchData->stdValueQuarks;
1477       curQ = &quarks[FIRSTSTD_QUARK];
1478       while ( (*stdQ = *curQ) != NULLQUARK ) stdQ++, curQ++;
1479    }
1480
1481    return False;    /* continue enumeration */
1482 }       /*$END$*/
1483
1484
1485 #if DOC
1486 /*========================================================*/
1487 $PFUNBEG$: DoCommonSrchDataPrep
1488 $1LINER$:  Prep srchData to search for a pattern
1489 $SUMMARY$: 
1490 $ARGS$:
1491 $RETURNS$:
1492 /*================================================$SKIP$==*/
1493 #endif
1494 static
1495 void DoCommonSrchDataPrep(
1496       __DtXlateSrchData * srchData,
1497       _DtXlateDb          db,
1498       const char *        platform,
1499       const int           version,
1500       const char *        operation)
1501 {       /*$CODE$*/
1502    int       verNum = version;       /* for lint */
1503
1504    /* zero the search data */
1505    memset(srchData,0,sizeof(__DtXlateSrchData));
1506
1507    /* set the db */
1508    srchData->db = db;
1509    
1510    /* build filter list for enumerating the db */
1511    if (verNum < MATCHALL_VER) verNum = MATCHALL_VER;
1512    srchData->platformStr = platform;
1513    srchData->platformQuark = (platform?XrmStringToQuark(platform):NULLQUARK);
1514    srchData->version = verNum;
1515    sprintf(srchData->verStr,"%d",verNum);
1516    srchData->operStr = operation;
1517    srchData->operStrLen = strlen(operation);
1518    srchData->operQuark = (operation ? XrmStringToQuark(operation) : NULLQUARK);
1519    srchData->lessThanQuark = XrmStringToQuark(LESSTHAN_STR);
1520    srchData->equalsToQuark = XrmStringToQuark(EQUALS_STR);
1521    srchData->containsQuark = XrmStringToQuark(CONTAINS_STR);
1522    srchData->moreThanQuark = XrmStringToQuark(MORETHAN_STR);
1523    srchData->inhibitQuark = XrmStringToQuark(INHIBIT_STR);
1524 }       /*$END$*/
1525
1526 /*========================================================*/
1527 /*=============== Public Xlate routines ==================*/
1528 /*========================================================*/
1529
1530
1531
1532 #if DOC
1533 /*========================================================*/
1534 $FUNBEG$:  _DtXlateOpenDb()
1535 $1LINER$:   Open a translation database
1536 $SUMMARY$: 
1537 Opens a translation resource database and returns a
1538 reference to it in ret_db.
1539
1540 Initializes the _DtXlateDb object to ready for use.
1541 If an error occurs, _DtXlateDb is set to NULL.
1542 $ARGS$:
1543 $RETURNS$:
1544 0:  no error occurred
1545 -1: if ret_db is NULL
1546     if XrmGetFileDatabase() failed on databaseName
1547     if malloc fails to alloc a db structure
1548 /*================================================$SKIP$==*/
1549 #endif
1550
1551 int  _DtXlateOpenDb(
1552        const char *  databaseName,
1553        _DtXlateDb *  ret_db)
1554 {       /*$CODE$*/
1555     XrmDatabase xrmDb;
1556     __DtXlateDbRec * dbRec = NULL;
1557     char * path = NULL;
1558
1559     if(NULL == ret_db) return -1;        /* RETURN */
1560     *ret_db = NULL;
1561
1562     /* Do NOT check for whether *ret_db is already
1563        a valid db; this is none of our affair. */
1564
1565     /* get an absolute path for the file */
1566     path = ExpandPath(databaseName);
1567
1568     if (NULL == path) return -1;        /* RETURN */
1569
1570     xrmDb = XrmGetFileDatabase(path);
1571     if (NULL == xrmDb) { free(path); return -1; } /* RETURN */
1572
1573     /* alloc a db ref */
1574     dbRec = (__DtXlateDbRec *) calloc(1,sizeof(__DtXlateDbRec));
1575     if(NULL == dbRec) 
1576     {
1577        XrmDestroyDatabase(xrmDb); 
1578        free(path);
1579        return -1;                        /* RETURN */
1580     }
1581
1582     /* and populate it */
1583     dbRec->xrmDb = xrmDb;
1584     dbRec->initGuard = INIT_OCCURRED;
1585
1586     /* check for debug mode */
1587     SetDebugModeState(dbRec);
1588
1589     if (dbRec->debugMode) 
1590        fprintf(stderr,"_DtXlateOpenDb: opened: %s; new db: %p\n",path,dbRec);
1591
1592     *ret_db = dbRec;
1593     free(path);
1594     return 0;       /* RETURN */
1595 }       /*$END$*/
1596
1597 #if DOC
1598 /*========================================================*/
1599 $FUNBEG$: _DtXlateOpenAndMergeDbs()
1600 $1LINER$: Opens a translation database and merges with earlier dbs
1601 $SUMMARY$: 
1602 Opens a translation resource database and returns a
1603 reference to it in ret_db.
1604
1605 Initializes the _DtXlateDb object to ready for use.
1606 $ARGS$:
1607 $RETURNS$:
1608 0:  no error occurred
1609 -1: if io_db is NULL
1610     if XrmGetFileDatabase() failed on databaseName
1611 /*================================================$SKIP$==*/
1612 #endif
1613
1614 int  _DtXlateOpenAndMergeDbs(
1615        const char *   databaseName,
1616        _DtXlateDb *   io_db)
1617 {       /*$CODE$*/
1618     XrmDatabase xrmDb;
1619     char * path;
1620     int  ret;
1621
1622     if(NULL == io_db) return -1;                /* RETURN */
1623
1624     /* if a db has not yet been opened */
1625     if(   NULL == *io_db
1626        || (*io_db)->initGuard != INIT_OCCURRED 
1627        || (*io_db)->xrmDb == NULL)
1628     {
1629        ret = _DtXlateOpenDb(databaseName,io_db); /* RETURN */
1630        if ( (*io_db) && (*io_db)->debugMode) 
1631           fprintf(stderr,"_DtXlateOpenAndMergeDb: "
1632                       "used _DtXlateOpenDb to open first file\n");
1633        return ret;      /* RETURN */
1634     }
1635        
1636     if ( (*io_db) && (*io_db)->debugMode) 
1637        fprintf(stderr,"_DtXlateOpenAndMergeDb: "
1638                       "target file: %s; existing db: %p\n",databaseName,*io_db);
1639
1640     /* a db has been opened, let's merge with it */
1641
1642     /* get an absolute path for the file */
1643     path = ExpandPath(databaseName);
1644
1645     if (NULL == path) goto Failed;     /* RETURN */
1646
1647     xrmDb = XrmGetFileDatabase(path);
1648     if (NULL == xrmDb) goto Failed;    /* RETURN */
1649
1650     /* merge and destroy xrmDb for me */
1651     XrmMergeDatabases(xrmDb,&(*io_db)->xrmDb);
1652
1653     /* check for debug mode */
1654     SetDebugModeState(*io_db);
1655
1656     if ((*io_db)->debugMode) 
1657        fprintf(stderr,"_DtXlateOpenAndMergeDb: "
1658                       "opened: %s; merged db: %p\n",path,*io_db);
1659
1660     free(path);
1661     return 0;      /* RETURN */
1662
1663 Failed:
1664     if ( (*io_db) && (*io_db)->debugMode) 
1665        fprintf(stderr,"_DtXlateOpenAndMergeDb: open failed; file: %s\n", 
1666           (path ? path : (databaseName ? databaseName : "NULL") ) );
1667     if (path) free(path);
1668     return -1;      /* RETURN */
1669 }       /*$END$*/
1670
1671 #if DOC
1672 /*========================================================*/
1673 $FUNBEG$: _DtXlateMergeDbs()
1674 $1LINER$: Merges two open dbs into one and closes the merged-in db.
1675 $SUMMARY$: 
1676 Merges two databases into one and closes the merged db.
1677
1678 The io_dbToMerge database must be a valid translation database.  
1679 The io_dbToMerge database is merged into the io_mergeIntoDb.  
1680 The io_mergeIntoDb may either be invalid or valid.  If invalid,
1681 the io_dbToMerge database is simply moved over to io_mergeIntoDb.
1682 If io_mergeIntoDb is valid, the entries in the io_dbToMerge
1683 database are merged into it and take precedence over entries in the
1684 io_mergeIntoDb, and the io_dbToMerge database is closed.
1685 $ARGS$:
1686 io_dbToMerge:    database to merge into io_mergeIntoDb
1687 io_mergeIntoDb:  database to hold merged result
1688 $RETURNS$:
1689 0:  no error occurred
1690 -1: if io_dbToMerge or io_mergeIntoDb is NULL
1691     if *io_dbToMerge is NULL or uninitialized
1692 /*================================================$SKIP$==*/
1693 #endif
1694
1695 int  _DtXlateMergeDbs(
1696        _DtXlateDb *  io_dbToMerge,
1697        _DtXlateDb *  io_mergeIntoDb)
1698 {       /*$CODE$*/
1699
1700     /* check args */
1701     if(   NULL == io_mergeIntoDb 
1702        || NULL == io_dbToMerge
1703        || NULL == *io_dbToMerge
1704        || (*io_dbToMerge)->initGuard != INIT_OCCURRED 
1705        || (*io_dbToMerge)->xrmDb == NULL)
1706        return -1;                             /* RETURN */
1707
1708     /* check for debug mode */
1709     if (   ((*io_mergeIntoDb) && (*io_mergeIntoDb)->debugMode) 
1710         || (*io_dbToMerge)->debugMode) 
1711        fprintf(stderr,"_DtXlateMergeDbs: "
1712                   "mergeIntoDb: %p; dbToMerge: %p\n",*io_mergeIntoDb,*io_dbToMerge);
1713
1714     /* if db_mergeIntoDb has not yet been opened */
1715     if(   NULL == *io_mergeIntoDb
1716        || (*io_mergeIntoDb)->initGuard != INIT_OCCURRED 
1717        || (*io_mergeIntoDb)->xrmDb == NULL)
1718     {
1719        /* just move dbToMerge into mergeIntoDb */
1720        *io_mergeIntoDb = *io_dbToMerge;
1721        DeleteDbMem(io_dbToMerge);
1722
1723        return 0;                                /* RETURN */
1724     }
1725        
1726     /* merge and destroy io_dbToMerge->xrmDb for me */
1727     XrmMergeDatabases((*io_dbToMerge)->xrmDb,&(*io_mergeIntoDb)->xrmDb);
1728     DeleteDbMem(io_dbToMerge);
1729
1730     /* check for debug mode */
1731     SetDebugModeState(*io_mergeIntoDb);
1732
1733     if ((*io_mergeIntoDb)->debugMode) 
1734        fprintf(stderr,"merged db: %p\n",*io_mergeIntoDb);
1735
1736     return 0;
1737 }       /*$END$*/
1738
1739 #if DOC
1740 /*========================================================*/
1741 $FUNBEG$: _DtXlateOpenAllDbs()
1742 $1LINER$: Open and merge all locale translation databases that can be found
1743 $SUMMARY$:
1744 DtXlateOpenAllDbs() locates all translation databases
1745 present in the search paths directories.
1746 $ARGS$:
1747 searchPaths:  ':' separated list of directories
1748 databaseName: name of the database file in those directories
1749 ret_db:       the reference to the open database is stored here
1750 $RETURNS$:
1751  0: at least one database was opened
1752 -1: no database was opened
1753 /*================================================$SKIP$==*/
1754 #endif
1755
1756 int  _DtXlateOpenAllDbs(
1757          const char * searchPaths,
1758          const char * databaseName,
1759          _DtXlateDb * ret_db)
1760 {       /*$CODE$*/
1761     const char * workStart = searchPaths;
1762     const char * separator = NULL;
1763     char         dbFile[MAXPATHLEN+1];
1764     int          ret = ~0;    /* all bits set */
1765
1766     /* cycle through the paths, opening each one */
1767     do
1768     {
1769        int  workLen = 0;
1770        const char * slash = NULL;
1771
1772        dbFile[0] = EOS;
1773
1774        /* isolate the next part of the path */
1775        _DtMBStrchr (workStart, PATH_SEPARATOR, -1, &separator);
1776        if (NULL == separator) _DtMBStrchr (workStart, EOS, -1, &separator);
1777        if (NULL == separator) break;           /* BREAK */
1778        workLen = separator - workStart;  /* dont include +1 for EOS */
1779
1780        /* copy over the path component */
1781        strncpy(dbFile,workStart,workLen);
1782        workStart = separator + 1;
1783
1784        /* add a slash to end of path component, if needed */
1785        *(dbFile+workLen) = EOS;  /* add an EOS for _DtMBStrrchr to find */
1786        _DtMBStrrchr(dbFile,DIR_SLASH,-1,&slash);
1787        if (slash != dbFile+workLen-1) /* is slash last char of path? */
1788        {
1789          *(dbFile+workLen) = DIR_SLASH;
1790          workLen++;
1791        }
1792
1793        /* append the filename and EOS */
1794        strcpy(dbFile+workLen,databaseName);
1795
1796 /*printf("Working on: %s\n", dbFile);  **DBG*/
1797
1798        /* open and merge the database with previously opened dbs */
1799        /* by ANDing, we determine whether at least one call returned 0 */
1800        ret &= _DtXlateOpenAndMergeDbs(dbFile,ret_db);
1801
1802     } while ( *separator != EOS );
1803
1804     if (*ret_db && (*ret_db)->debugMode) 
1805        fprintf(stderr,"_DtXlateOpenAllDbs: completed\n"
1806                       "  srchpaths: %s; db file: %s\n",searchPaths,databaseName);
1807
1808     return (ret == 0 ? 0 : -1);  /* ret != 0 ==> no db was opened */
1809 }       /*$END$*/
1810
1811
1812 #if DOC
1813 /*========================================================*/
1814 $FUNBEG$: _DtXlateCloseDb()
1815 $1LINER$: Close an open translation database
1816 $SUMMARY$:
1817 _DtXlafteCloseDb() releases all memory associated with
1818 the translation database.  Further use of the database
1819 object is an error.
1820 $ARGS$:
1821 $RETURNS$:
1822  0:  database was valid and has been closed
1823 -1:  invalid database pointer
1824 /*================================================$SKIP$==*/
1825 #endif
1826
1827 int _DtXlateCloseDb(
1828        _DtXlateDb * io_db)
1829 {       /*$CODE$*/
1830      __DtXlateDbRec * dbRec;
1831      
1832      if(   NULL == io_db 
1833         || NULL == (dbRec = *io_db)        /* dbRec assigned */
1834         || dbRec->initGuard != INIT_OCCURRED) 
1835         return -1;                     /* RETURN */
1836  
1837      XrmDestroyDatabase(dbRec->xrmDb);
1838
1839      if (dbRec->debugMode) fprintf(stderr,"_DtXlateCloseDb: %p\n",dbRec);
1840
1841      /* zero out object mem and free it */
1842      DeleteDbMem(io_db);
1843
1844      return 0;
1845 }       /*$END$*/
1846
1847
1848 #if DOC
1849 /*========================================================*/
1850 $FUNBEG$: _DbXlateStdToOpValue()
1851 $1LINER$:  Translates a standardized spec to an operation-specific value
1852 $SUMMARY$: 
1853 Looks up the best translation of the standard value for an
1854 operation and places a pointer to the translation string
1855 at the location pointed to by ret_opValue.
1856
1857 The translated string was allocated using malloc() and
1858 must be freed when no longer needed.
1859
1860 If ret_opValue is NULL, the function merely verifies that
1861 a valid translation exists.
1862 $ARGS$:
1863 db:             a translation database
1864 platform:       the platform string (see _DtXlateGetXlateEnv())
1865 version:        the version number (see _DtXlateGetXlateEnv())
1866 operation:      the operation of interest, e.g. "setlocale"
1867 stdValue:       the standard value pattern
1868 ret_opValue:    location where ptr to translated string is stored
1869 ret_reserved:   reserved for future use
1870 $RETURNS$:
1871  0: translation found
1872 -1: invalid database (NULL ptr, not opened)
1873     no operation was specified
1874     query failed to find a match
1875 /*================================================$SKIP$==*/
1876 #endif
1877
1878 int _DtXlateStdToOpValue(
1879        _DtXlateDb        db,
1880        const char *      platform,
1881        const int         version,
1882        const char *      operation,
1883        const char *      stdValue,
1884        char * *          ret_opValue,
1885        void *            ret_reserved)
1886 {       /*$CODE$*/
1887    __DtXlateSrchData srchData;
1888    XrmQuark  empty = NULLQUARK;
1889
1890    if (   NULL == db 
1891        || db->initGuard != INIT_OCCURRED
1892        || NULL == operation
1893        || operation[0] == EOS)
1894        return -1;                                   /* RETURN error */
1895
1896    /* prep srch data for search */
1897    DoCommonSrchDataPrep(&srchData,db,platform,version,operation);
1898
1899    /* handle a rare case */
1900    if (NULL == stdValue)
1901    {
1902        if (db->debugMode) fprintf(stderr,"_DtXlateStdToOpValue: NULL std value\n");
1903        if (ret_opValue) *ret_opValue = NULL;
1904        return -1;                                  /* RETURN error */
1905    }
1906
1907    /* build std value list for use during comparison */
1908    srchData.stdValueQuarks[0] = NULLQUARK;
1909    if (NULL != stdValue && stdValue[0] != EOS)
1910       XrmStringToQuarkList(stdValue,srchData.stdValueQuarks);
1911
1912    if (db->debugMode)
1913       fprintf(stderr,"_DtXlateStdToOpValue: %s.%d.%s.%s: <op>\n",
1914           platform,version,operation,stdValue);
1915
1916    /* scan through this Db looking for matches and put in search */
1917    XrmEnumerateDatabase(db->xrmDb,&empty,&empty,
1918               XrmEnumAllLevels, FindStdToOpMatchCB, (XPointer) &srchData);
1919
1920    if (   srchData.opValue != NULL
1921        && srchData.bestTransType != __DtXLATE_TYPE_INHIBIT )
1922    {
1923        char * opValue;
1924        
1925        if (ret_opValue == NULL)
1926        {
1927           if (db->debugMode) fprintf(stderr,"translation exists\n");
1928           return 0;  /* RETURN: translation exists */
1929        }
1930        
1931        /* alloc the string to return */
1932        opValue = strdup(srchData.opValue);
1933
1934        if (db->debugMode) fprintf(stderr,"raw opval:%s\n",opValue);
1935
1936        /* do quote and escape removal and ref replacement in the opValue */
1937        if ( srchData.bestTransType == __DtXLATE_TYPE_REGEX )
1938           StripMetaAndReplaceEscSubex(&opValue,False,srchData.bestSpecRefs);
1939        else
1940           StripMetaAndReplaceEscSubex(&opValue,False,NULL);
1941
1942        if (db->debugMode) fprintf(stderr,"op value:%s\n",opValue);
1943
1944        *ret_opValue = opValue;
1945        return 0;                              /* RETURN: search successful */
1946    }
1947    return -1;                                /* RETURN: search failed */
1948 }       /*$END$*/
1949
1950
1951 #if DOC
1952 /*========================================================*/
1953 $FUNBEG$: _DbXlateOpToStdValue()
1954 $1LINER$:  Translates an operation-specific value to a standardized one
1955 $SUMMARY$: 
1956 Looks up the best translation of the operation value for an
1957 operation and places a pointer to the standard string
1958 at the location pointed to by ret_stdValue.
1959
1960 The standard string was allocated using malloc() and
1961 must be freed when no longer needed.
1962
1963 If ret_stdValue is NULL, the function merely verifies that
1964 a valid translation exists.
1965 $ARGS$:
1966 db:             a translation database
1967 platform:       the platform string (see _DtXlateGetXlateEnv())
1968 version:        the version number (see _DtXlateGetXlateEnv())
1969 operation:      the operation of interest, e.g. "setlocale"
1970 opValue:        the operation-specific value pattern
1971 ret_stdValue:   location where ptr to standard string is stored
1972 ret_reserved:   reserved for future use
1973 $RETURNS$:
1974  0: translation found
1975 -1: invalid database (NULL ptr, not opened)
1976     no operation was specified
1977     query failed to find a match
1978 /*================================================$SKIP$==*/
1979 #endif
1980
1981 int _DtXlateOpToStdValue(
1982        _DtXlateDb        db,
1983        const char *      platform,
1984        const int         version,
1985        const char *      operation,
1986        const char *      opValue,
1987        char * *          ret_stdValue,
1988        void *            ret_reserved)
1989 {       /*$CODE$*/
1990    __DtXlateSrchData srchData;
1991    XrmQuark  empty = NULLQUARK;
1992    char      lhs[MAXLHSSIZE];
1993
1994    if (   NULL == db 
1995        || db->initGuard != INIT_OCCURRED
1996        || NULL == operation
1997        || operation[0] == EOS)
1998        return -1;                                   /* RETURN error */
1999
2000    /* prep srch data for search */
2001    DoCommonSrchDataPrep(&srchData,db,platform,version,operation);
2002
2003    /* after check on value, store op value for use during comparison */
2004    /* not meaningful to check for a NULL value */
2005    if (NULL == opValue)
2006    {
2007       if (db->debugMode) fprintf(stderr,"_DtXlateOpToStdValue: NULL op value\n");
2008       if (ret_stdValue) *ret_stdValue = NULL;
2009       return -1;                                   /* RETURN error */
2010    }
2011    srchData.opValue = opValue;
2012
2013    if (db->debugMode)
2014       fprintf(stderr,"_DtXlateOpToStdValue: %s.%d.%s.<std>: %s\n",
2015           platform,version,operation,opValue);
2016
2017    /* scan through this Db looking for matches and put in search */
2018    XrmEnumerateDatabase(db->xrmDb,&empty,&empty,
2019               XrmEnumAllLevels, FindOpToStdMatchCB, (XPointer) &srchData);
2020
2021    if (   srchData.stdValueQuarks[0] != NULLQUARK
2022        && srchData.bestTransType != __DtXLATE_TYPE_INHIBIT )
2023    {
2024       XrmQuarkList stdQ;
2025       char * stdValue = lhs;
2026       int    stdValueLen = 0;
2027
2028       if (ret_stdValue == NULL)
2029       {
2030          if (db->debugMode) fprintf(stderr,"translation exists\n");
2031          return 0;  /* RETURN: translation exists */
2032       }
2033
2034       /* make a STDVALUE_SEPARATOR separated string out of the std data */
2035       for ( stdQ = srchData.stdValueQuarks;
2036             *stdQ != NULLQUARK;
2037             stdQ++ )
2038       {
2039          const char * str = XrmQuarkToString(*stdQ);
2040          int strLen;
2041          if (NULL == str || str[0] == EOS) continue;
2042          
2043          strLen = strlen(str);
2044          if (stdValue != lhs) 
2045              {*stdValue++ = STDVALUE_SEPARATOR; stdValueLen++; }
2046          if ((stdValueLen + strLen) > sizeof(lhs) )
2047              strLen = sizeof(lhs)-stdValueLen;
2048          strncpy(stdValue,str,strLen);
2049          stdValueLen += strLen;
2050          stdValue += strLen;
2051       }
2052       *stdValue = EOS;
2053
2054       if (db->debugMode) fprintf(stderr,"raw stdval:%s\n",lhs);
2055
2056       /* do quote and escape removal and ref replacement in the stdValue */
2057       stdValue = strdup(lhs);        /* reset stdValue ptr */
2058       if ( srchData.bestTransType == __DtXLATE_TYPE_REGEX )
2059           ReplaceMatchallSubex(&stdValue,srchData.bestSubEx,srchData.opValue);
2060
2061       if (db->debugMode) fprintf(stderr,"std value:%s\n",stdValue);
2062
2063       *ret_stdValue = stdValue;
2064       return 0;                              /* RETURN: search successful */
2065    }
2066    return -1;                                /* RETURN: search failed */
2067 }       /*$END$*/
2068
2069
2070
2071 #if DOC
2072 /*========================================================*/
2073 $FUNBEG$: _DtXlateGetXlateEnv()
2074 $1LINER$: Get the DtXlate compilation and execution environments.
2075 $SUMMARY$:
2076 _DtXlateGetXlateEnv() recovers the identity of the application
2077 current platform, the version value of the application
2078 execution environment, and the version value of the operating
2079 system version for which DtXlateGetXlateEnv() was
2080 compiled. These values can be used in formulating queries,
2081 especially for the _DtXlateStdToOpValue() query.
2082
2083 The technique used by this routine is as follows.  Using
2084 uname(2), the routine retrieves the sysname, release, and version
2085 strings.  The sysname is used as the platform name.  The
2086 release and version strings are concatenated in that order
2087 and as treated below as the <op-rel-ver>.  An OpToStd translation
2088 looks for a match to
2089     <sysname>.?.version.<.<std-version>: <op-rel-ver>
2090  or
2091     <sysname>.?.version.=.<std-version>: <op-rel-ver>
2092
2093 If no match is found, the next fallback position is to
2094 get the specification with the same sysname and the highest
2095 std-version integer value.  If no specifications exist for
2096 that sysname, then sysname is set to the empty string and
2097 std-version is -1.
2098
2099 ret_AppExecEnvPlatform should point to a character array at least 
2100 _DtPLATFORM_MAX_LEN characters long.  The sysname is copied to it.
2101
2102 ret_AppExecEnvVersion is given the integer value of the
2103 std-version recovered from the translation.
2104
2105 ret_XlateCompiledForOSVersion is given the integer value
2106 determined by using the OSMajorVersion and OSMinorVersion
2107 constants defined by the build environment of _DtXlate as follows:
2108
2109 #define _STR(V) #V
2110 #define STR(V) _STR(V)
2111       sprintf(buf,"%s%s%s", STR(OSMAJORVERSION), 
2112                      nl_langinfo(RADIXCHAR), STR(OSMAJORVERSION));
2113       verNum = (int) (100.0 * atof(buf));
2114
2115 For example:
2116 OSMAJORVERSION & OSMINORVERSION are compile-time constants
2117 that must be defined as part of the build environment.
2118 It is assumed that these constants are of the form:
2119
2120    e.g. Sun 5.3
2121    #define OSMAJORVERSION        5
2122    #define OSMINORVERSION        3
2123    
2124    530 = (int) 100.0 * atof("5.3");
2125    
2126    e.g. HP-UX 8.09
2127    #define OSMAJORVERSION         8
2128    #define OSMINORVERSION         09
2129    
2130    809 = (int) 100.0 * atof("8.09");
2131    
2132 Note that it may be necessary for the application to determine
2133 the version number of an operation in some platform-specific
2134 and operation-specific manner, for example using a library
2135 version value.  In many cases, however, the O.S. version value 
2136 for which _DtXlate was compiled will be sufficient
2137 when identifying version numbers for standard development
2138 environment libraries, such as libc.
2139
2140 $EXAMPLE$:
2141 char * 
2142 xlateStd2Op(_DtXlateDb db,Boolean runtimeOp, char * operation,char * stdVal)
2143 {
2144    char platform[_DtPLATFORM_MAX_LEN];
2145    int  version;
2146    char * opVal = NULL;
2147    int    ret;
2148
2149    if (runtimeOp)
2150       ret=_DtXlateGetXlateEnv(db,platform,&version,NULL);
2151    else
2152       ret=_DtXlateGetXlateEnv(db,platform,NULL,&version);
2153    if (ret == 0)
2154       _DtXlateStdToOpValue(db,platform,version,operation,stdVal,&opVal,NULL);
2155    return opVal;       /* will be NULL if error occurred */
2156 }
2157
2158 $ARGS$:
2159 ret_AppExecEnvPlatform:  pts to a string at least _DtPLATFORM_MAX_LEN long
2160                          that will hold the string uname(2) returns for sysname
2161 ret_AppExecEnvVersion:   pts to an integer that will receive the platform
2162                          standardized version number, as determined by
2163                          a translation on uname(2) release+version.
2164 ret_XlateCompiledForOSVersion:   pts to an integer that will receive the
2165                          operating system version for which _DtXlate was
2166                          compiled using OSMajorVersion * 100 + OSMinorVersion
2167 Any of the arguments may be NULL.
2168 $RETURNS$:
2169  0: if no error occurred
2170 -1: if no translation was possible to get the AppExecEnvVersion
2171 /*================================================$SKIP$==*/
2172 #endif
2173
2174 int _DtXlateGetXlateEnv(
2175          _DtXlateDb db,
2176          char *     ret_AppExecEnvPlatform,
2177          int *      ret_AppExecEnvVersion,
2178          int *      ret_XlateCompiledForOSVersion)
2179 {       /*$CODE$*/
2180    struct utsname names;
2181    int    ret = 0;
2182    char * platform = "NULL";
2183    int    execver = -1;
2184    int    compver = -1;
2185
2186    /* get host specifics */
2187    uname(&names);
2188
2189    /* first get execution host name */
2190    if (ret_AppExecEnvPlatform) 
2191    {
2192       strncpy(ret_AppExecEnvPlatform,names.sysname,_DtPLATFORM_MAX_LEN-1);
2193       ret_AppExecEnvPlatform[_DtPLATFORM_MAX_LEN-1] = EOS;
2194       platform=ret_AppExecEnvPlatform;
2195    }
2196
2197    /* then look up version number of execution host */
2198    if (ret_AppExecEnvVersion) 
2199    {
2200 #if defined(sun) || defined(_AIX) || defined(USL) || defined(__uxp__) || defined(__osf__) || defined(linux)
2201       char version[SYS_NMLN+SYS_NMLN+2];
2202 #else
2203       char version[UTSLEN+UTSLEN+2];
2204 #endif
2205       char * stdVer = NULL;
2206       int  verNum = MATCHALL_VER;
2207
2208       /* cat release version and do a translation on it to a std value */
2209       /* then convert the std value to a integer */
2210       strcpy(version,names.release);
2211       strcat(version,names.version);
2212       ret = _DtXlateOpToStdValue(db,names.sysname,NULL,
2213                    _DtXLATE_OPER_VERSION,version,&stdVer,NULL);
2214       if (ret == 0)
2215       {
2216          if (sscanf(stdVer,"%d",&verNum) != 1) verNum = MATCHALL_VER;;
2217          free(stdVer);
2218       }
2219       *ret_AppExecEnvVersion = verNum;
2220       execver = verNum;
2221    }
2222   
2223    /* then look up version number of execution host */
2224    if (ret_XlateCompiledForOSVersion) 
2225    {
2226       char buf[MAXINTSTRSIZE];
2227
2228 #define _STR(V) #V
2229 #define STR(V) _STR(V)
2230
2231       /*=========================== 
2232          OSMAJORVERSION & OSMINORVERSION are compile-time constants
2233          that must be defined as part of the build environment. 
2234          It is assumed that these constants are of the form:
2235
2236           e.g. Sun 5.3
2237           #define OSMAJORVERSION        5
2238           #define OSMINORVERSION        3
2239           
2240              530 = (int) (100.0 * atof("5.3"));
2241           
2242           e.g. HP-UX 8.09
2243           #define OSMAJORVERSION         8
2244           #define OSMINORVERSION         09
2245           
2246              809 = (int) (100.0 * atof("8.09"));
2247        ===========================*/
2248
2249 #if !defined(OSMAJORVERSION) || !defined(OSMINORVERSION) || OSMAJORVERSION == 0
2250 #error OSMAJORVERSION and/or OSMINORVERSION not defined
2251 #endif
2252
2253       sprintf(buf,"%s%s%s", STR(OSMAJORVERSION), 
2254                      nl_langinfo(RADIXCHAR), STR(OSMINORVERSION));
2255
2256       *ret_XlateCompiledForOSVersion = (int) (100.0 * atof(buf));
2257       compver = *ret_XlateCompiledForOSVersion;
2258    }
2259
2260    if (db->debugMode) 
2261       fprintf(stderr,"_DtXlateGetXlateEnv: "
2262                      "Platform: %s; Execution Ver: %d; Compiled Ver: %d\n",
2263                       platform,execver,compver);
2264
2265    return ret;
2266 }       /*$END$*/
2267
2268
2269 /*========================================================*/
2270 /*================ Introductory Info =====================*/
2271 /*========================================================*/
2272
2273 #if DOC
2274 /*========================================================*/
2275 $INTROBEG$: _DtXlate family
2276 $1LINER$: API to translate any value to a standard value and back again
2277 $SUMMARY$:
2278 _DtXlate is a collection of routines that allow translation
2279 between platform, version, and operation specific values
2280 into standard values and back again.  Translation is
2281 based on the contents of database files specified using
2282 Xrm-style resources.  The semantics of the translation
2283 are a combination of standard semantics for specifying
2284 platform, version, operation, and translation type, and
2285 caller-specific semantics for the standard value format
2286 and operation values.
2287
2288 The API contains routines to process translation databases
2289 in a useful manner.
2290
2291 _DtXlateOpenDb() opens a particular translation database
2292 _DtXlateOpenAndMergeDb() opens a particular database and
2293   merges it with an already opened one, overriding any
2294   repeated specifications.
2295 _DtXlateOpenAllDbs() opens all occurances of a translation
2296    database found in a search path and cumulatively merges 
2297    the contents, allowing for the override of existing 
2298    specifications as well as the addition of new ones.
2299 _DtXlateCloseDb() closes an open database
2300 _DtXlateOpToStdValue() translates an platform, version,
2301    and operation specific value into a standard value.
2302 _DtXlateStdToOpValue() translates a standard value into
2303    a platform, version, and operation specific value.
2304 /*=$END$================================================*/
2305 #endif
2306
2307 #if DOC
2308 /*========================================================*/
2309 $INTROBEG$: Design and Implementation Considerations
2310 $1LINER$: Factors influencing design and implementation
2311 $SUMMARY$:
2312 The syntax of the translation specification table is designed
2313 to be compatible with Xrm resource specifications.  This
2314 allows Xrm to be used to load, parse, and merge the databases
2315 for processing.  This also causes the specifications to be
2316 case-sensitive, as case is preserved by Xrm and is used when
2317 processing queries.
2318
2319 However, due to the semantics of translation specifications
2320 and limitations of Xrm, XrmGetResource() queries generally
2321 will not be useful.  Rather, a pattern matching API is provided
2322 that implements the query capability used for the translations.
2323 The initial implementation will use XrmEnumerateDatabase().
2324
2325 Using Xrm leads to an in-memory approach to translation, meaning
2326 that all the resources files are parsed and loaded into memory
2327 as the first step of a translation.  A line-at-a-time approach,
2328 that process one line at a time directly from the file in a
2329 grep- or awk-like manner, is likely more memory-efficient.
2330
2331 Note that the line-at-a-time approach does not avoid parsing
2332 all the specification files, as the API supports inheritence
2333 and override of specifications.  Hence, to ensure the correct
2334 value, the entire file set must be processed.  Note also that
2335 in a situation where translations will be repeatedly performed,
2336 the Xrm in-memory approach may be more time-efficient, as the
2337 files need only be parsed once, and then are utilized repeatedly.
2338
2339 Because of time constraints, a line-at-a-time approach will not
2340 be used for the first implementation, as Xrm provides all the
2341 necessary parsing, filtering, and symbol hashing routines
2342 for free.  Given the likely large size of the tables and their
2343 infrequent use, a line-at-a-time approach is likely the better
2344 choice.
2345
2346 /*=$END$================================================*/
2347 #endif
2348
2349
2350 #if DOC
2351 /*========================================================*/
2352 $INTROBEG$: translation BNF syntax and semantics
2353 $1LINER$: _DtXlate translation specification syntax and semantics
2354 $SUMMARY$:
2355    BNF Syntax of Translation Specification
2356    =======================================
2357
2358 <specfile>   ::= (<xlatespec> | <comment> | <cr>)*
2359 <xlatespec>  ::= <platform>.<version>.<operations>.<transtype>
2360                       .<stdvalue>:<opvalue> (<comment>|<cr>)
2361 <platform>   ::= <identifier> | <matchall>
2362 <version>     ::= <number> [+ | (- <number>)] | <matchall>
2363 <operations> ::= <identifier> [',' <identifier>]* | <matchall>
2364 <transtype>  ::= '<' | '=' | '~' | '>' | '0'
2365 <stdvalue>   ::= <identifier> [.<identifier>]*
2366 <langterr>   ::= <identifier> | <matchall>
2367 <codeset>    ::= <identifier> | <matchall>
2368 <modifier>   ::= <identifier> | <matchall>
2369 <opvalue>    ::= (<vischars>|<metachar>)+ | '"'(<anychar>|<metachar>)+'"'
2370
2371 <matchall>   ::= '?'[<number>]
2372 <cr>         ::= '\n'
2373 <comment>    ::= '!' [^<cr>]* <cr>
2374 <number>     ::= [0-9]+
2375 <identifier> ::= [-_a-ZA-Z0-9]
2376 <vischar>    ::= any non-whitespace character; meta and regular
2377                  expression chars must be escaped with "\\"
2378 <anychar>    ::= any printable character; meta and regular
2379                  expression chars must be escaped with "\\"
2380 <metachar>   ::= '!' | '"' | <backslash> | <valex> | <regex>
2381 <valex>      ::= <backslash><number>
2382 <regex>      ::= see regexp(5)
2383 <backslash>  ::= the backslash character (\).
2384
2385    Semantics of the Translation Specification
2386    ==========================================
2387
2388 <specfile> : a file containing zero or more translation specifications
2389 or comments
2390
2391 <xlatespec> : a translation specification defines a fully qualified
2392 string value which may be recovered by query pattern matching the
2393 specification.  The qualifiers identify the semantics of the usage
2394 of the value, allowing queries to be meaningful.  The entire xlatespec
2395 must be on one line of the file.
2396
2397 <platform> : a CDE-standardized identifier for an operating system
2398 platform.  The platform need not be supported by CDE, but CDE must
2399 have standardized an identifier for the platform in order for it to
2400 be used.  For example, candidate platform identifiers are "HP-UX",
2401 "AIX", "SunOS", "Solaris", "SCO", etc.  These identifiers are the
2402 values returned by uname(2):utsname.sysname and uname(1) -s, and
2403 I propose CDE standardize on using these values.
2404
2405 The identifier string and matching constant value must be added to
2406 the source code of the translation routine in order for it to be
2407 recognized.  This allows control over the platform registry
2408 and enables automatic generation of the platform on the execution
2409 host through the use of #ifdef <PLATFORM_CONST> in the source code.
2410
2411 <version> : a platform-specific numeric value or range of numberic
2412 values that allows the value to be qualified to a particular version
2413 and release (version + release = version) or range of versions and
2414 releases of the platform.  The version numbers must be integral and
2415 suitable for numeric comparison.  The '+' can be used to specify an
2416 open upwards bound, as in 900+.  The '- <number>' can be used to
2417 specify an upper bound, as in 900-999.  If no range is specified,
2418 the version number must match exactly to the query pattern and/or
2419 execution environment.
2420
2421 Platforms may not directly provide version numbers in a numeric
2422 format suitable for use when comparing with a translation
2423 specification.  As part of the translation API source code,
2424 conversion routines must be supplied that translate the platform-
2425 specific values, such as those provided by uname(2), into a
2426 translation specification-compliant format suitable for comparison
2427 to <version>.  Understanding the conversion routines operation
2428 will be necessary to ensure correct translation specifications.
2429
2430 <operations> : a CDE-standardized identifier for the operation(s) to
2431 which the value applies.  The operation(s) need not be supported by every
2432 platform, but CDE must have standardized an identifier for the operation
2433 in order for it to be used.  More than one identifer may be included by
2434 concatenating them using the ',' separator, eg. "iconv1,iconv3".
2435
2436 <transtype> : The <transtype> field records the direction of
2437 translation that the specification supports:  '<', '=', '~', '>', '0'.
2438 Use '<' if the <opvalue> contains a regular expression; the
2439 specification may only be used to match an operation value in
2440 a _DtLcxXlateOpToStd() translation.  Use '>' if the <opvalue>
2441 contains value replacement expressions using values from
2442 the <stdvalue> strings; the specification may only be used by a
2443 _DtLcxXlateStdToOp() translation.  Use '=' if the <opvalue>
2444 is a pure string that may be used for either OpToStd() or
2445 StdToOp() translations, and the  specification op-value must exactly
2446 match the op-value of the translation request.  use '~' if the
2447 <opvalue> is a pure string that may be used for either OpToStd() or
2448 StdToOp() translations, and the specifation op-value must be
2449 a string within the op-value of the translation request.
2450 Use the '0' translation to represent that this particular 
2451 translation should result in an error, that is should not result 
2452 in a translation.
2453
2454 Translations are two-way operations: from a host- and
2455 operation-specific value to standardized value, and back again.
2456 In many cases, a straight string value for string value translation
2457 is sufficient.
2458
2459 But often, more sophisticated pattern matching and value generation
2460 mechanisms are useful to keep the table size small and as general
2461 as possible.  In these cases, wildcard and regular expression
2462 operators are useful.  Unfortunately, these specifications are not
2463 bi-direction.  That is, the pattern matching string can be used
2464 to match against a pattern, but not to generate a result string.
2465 For example, the vi expression  s/he*lo/hello/ matches "hello"
2466 as well as "hey look out below", but the reverse direction of
2467 "hello"->"he*lo" will not work.
2468
2469 <stdvalue> : a sequence of one or more '.'-separated CDE-standardizd
2470 identifiers or matchall characters.  This represents the canonical
2471 string used as a standard representation of a semantic value that
2472 may vary in different situations.
2473
2474 <opvalue> : a string that has different uses depending on the
2475 value of <transtype>.  If transtype is '<', the string is used
2476 as a regular expression to match the opValue in _DtLcxXlateOpToStd()
2477 requests.  If the transtype is '>', the string is used as a
2478 replacement expression to generate a opValue in _DtLcxXlateStdToOp().
2479 If transtype is '=' or '~', the string is used as a straight string to
2480 both match for the OpToStd translation and as a value for
2481 the StdToOp translation.
2482 %
2483 If the opvalue contains whitespace, the opvalue must be enclosed in
2484 double quote characters.  If the opvalue contains any meta characters
2485 that should not be treated as meta characters, they must be escaped by
2486 two preceding backslashs.Lack of an opvalue is not an error, but will
2487 be ignored during matches and return an empty string in a StdToOp
2488 translation; to specify that a given translation does not exist, the
2489 '0' translation type should be used or the specification should not
2490 occur.
2491
2492 <comment> : a comment begins on an empty line with an unescaped !
2493 and continues to the end of the line.
2494
2495 <matchall> : The matchall character is a universal quantifier, meaning
2496 that it symbolizes all possible values of the field where it is used.
2497 The matchall character may occur only in the qualifers side of
2498 a translation specification.  Traditionally, the matchall character
2499 has been '*', but because of the semantics of Xrm, if the '*' is used
2500 as the matchall, Xrm does not restrict enumeration as needed.
2501
2502 The matchall character may be followed by a number when the <transtype>
2503 is '>'.  When this occurs, the string that matches the matchall
2504 character may be referenced in the <opvalue> by using the sequence
2505 \<number>, as in "\1".  The occurance is replaced with the matched
2506 string.
2507 /*=$END$================================================*/
2508 #endif
2509
2510 #if DOC
2511 /*========================================================*/
2512 $INTROBEG$: translation specification examples
2513 $1LINER$: examples of _DtXlate translation specifications
2514 $EXAMPLE$:
2515
2516 !! These are examples of bi-directional straight translations
2517 HP-UX.900-999.setlocale.=.en_US.hp-roman8:        american
2518 HP-UX.900-999.setlocale.=.en_US.iso88591:         american.iso88591
2519 HP-UX.900-999.setlocale.=.nl_BE.hp-roman8.fold:   dutch@fold
2520 HP-UX.900-999.setlocale.=.nl_BE.hp-roman8.nofold: dutch@nofold
2521
2522 !! These are examples of OpToStd translations utilizing regular 
2523 !! expression patterns to match the search value.
2524 HP-UX.?.version.<.900:   "A\\.09\\..*"     !! any HPUX 9.x version
2525 HP-UX.?.version.<.807:   "A\\.08\\.07.*"
2526 HP-UX.?.version.<.800:   "A\\.08\\..*"
2527 AIX.?.version.<.320:     "2 3"
2528 AIX.?.version.<.300:     "[0-9] 3"
2529 SunOS.?.version.<.530:   "5\\.3.*"
2530 SunOS.?.version.<.500:   "5\\..*"
2531
2532 !! These are examples of StdToOp translations utilizing matchall
2533 !! specifiers in the std value to match the search value.
2534 HP-UX.900+.iconv1.>.?.iso88596:     arabic8
2535 HP-UX.900+.iconv1.>.?.iso88597:     greek8
2536 HP-UX.900+.iconv1.>.?.hp-kana8:     kana8
2537 HP-UX.900+.iconv1.>.?.hp-roman8:    roman8
2538
2539 !! The following examples use value replacement as part of their
2540 !! specifications.  Using this can lead to much sparser tables, but
2541 !! it depends on op-specific and std values sharing the same strings.
2542 HP-UX.900-999.setlocale.<.nl_BE.hp-roman8.?1:     [dD][uU][tT][cC][hH]@\\(.*\\)
2543 !HP-UX.900-999.setlocale.<.nl_BE.hp-roman8.?1:     dutch@\\(.*\\)
2544 HP-UX.1000+.setlocale.>.?1.hp-roman8:    \\1.roman8     !! all non-modif cases
2545 HP-UX.1000+.setlocale.>.?1.hp-roman8.?2: \\1.roman8@\\2 !! all modif cases
2546
2547 /*=$END$================================================*/
2548 #endif
2549
2550
2551 #if DOC
2552 /*========================================================*/
2553 $INTROBEG$: _DtXlate example usage
2554 $1LINER$: Examples of how to _DtXlate
2555 $EXAMPLE$:
2556 #include <XlationSvc.h>
2557 main()
2558 {
2559    _DtXlateDb db = NULL;
2560    int  ret;
2561    char plat[_DtPLATFORM_MAX_LEN];
2562    int  execver;
2563    int  compver;
2564    char * val = NULL;
2565    char * str = NULL;
2566    char empty = 0;
2567 #define OPER_YOUROP "myop"
2568
2569    env = getenv("MYPATH");
2570    if (env == NULL) env = &empty;
2571
2572    ret = _DtXlateOpenAllDbs(env,"myfile.xlt",&db);
2573
2574    ret = _DtXlateGetXlateEnv(db,plat,&execver,&compver);
2575    printf("Platform: %s\nExec Ver: %d\nComp Ver: %d\n",
2576                     plat,execver,compver);
2577
2578    ret = _DtXlateStdToOpValue(db,plat,compver,OPER_YOUROP,
2579                      str="en_US.hp-roman8",&val,NULL);
2580    if (ret==0) printf("setlocale(%s) xlation=%s\n", str, val);
2581    else printf("no xlation\n", val);
2582
2583    ret = _DtXlateOpToStdValue(db,plat,execver,OPER_YOUROP,
2584                      str="american",&val,NULL);
2585    if (ret==0) printf("setlocale(%s) xlation=%s\n", str, val);
2586    else printf("no xlation\n", val);
2587
2588    ret = _DtXlateCloseDb(&db);
2589 }
2590 /*=$END$================================================*/
2591 #endif