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