/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $TOG: XlationSvc.c /main/13 1999/10/14 15:59:35 mgreess $ */ /**************************************************************************** $FILEBEG$: XlationSvc.c $PROJECT$: Cde 1.0 $COMPONENT$: DtXlate service $1LINER$: Implements a translation service using tables and regex search $COPYRIGHT$: (c) Copyright 1993, 1994 Hewlett-Packard Company (c) Copyright 1993, 1994 International Business Machines Corp. (c) Copyright 1993, 1994 Sun Microsystems, Inc. (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of Novell, Inc. $END$ **************************************************************************** ************************************<+>*************************************/ #include #include #include #include #include #if defined(sun) #include #endif #include /* INT_MAX */ #include /* for getpw... */ #include /* for uname */ #include /* MAXPATHLEN */ /* for RADIXCHAR and nl_langinfo */ #if defined(__linux__) # define RADIXCHAR MON_DECIMAL_POINT #endif #include #if defined(sun) #include /* for compile,advance */ #else #include /* for regcomp,regexec */ #endif /* for Xrm */ #include #include /*================================================================= $SHAREDBEG$: This header appears in all appropriate DtXlate topics =======================================================$SKIP$======*/ /*$INCLUDE$*/ #include "XlationSvc.h" #include "LocaleXlate.h" /*$END$*/ /**** For extensive Introductory info, go to the end of this file ****/ /*========================================================*/ /*====================== Constants =======================*/ /*========================================================*/ /*=============== private =================*/ /*#define DBG_MATCHING ** if defined, debugging matching stages are compiled */ #define DEBUG_XLATE_NAME "dtXlate.debugDtXlate" #define DEBUG_XLATE_CLASS "DtXlate.DebugDtXlate" #define EOS '\0' /* A "random" number used to ensure that the Db has been initalized */ #define INIT_OCCURRED 2329479 #define PATH_SEPARATOR ':' #define DIR_SLASH '/' #define LESSTHAN_STR "<" #define EQUALS_STR "=" #define CONTAINS_STR "~" #define MORETHAN_STR ">" #define INHIBIT_STR "0" #define MATCHALL_CHAR '?' #define ESCAPE_CHAR '\\' #define COMMENT_CHAR '!' #define QUOTE_CHAR '\"' #define OPER_SEPARATOR ',' #define STDVALUE_SEPARATOR '.' #define MATCHALL_VER 0 #define PLATFORM_QUARK 0 /* index offsets into quarklist */ #define VERSION_QUARK 1 #define OPERS_QUARK 2 #define DIRECTION_QUARK 3 #define FIRSTSTD_QUARK 4 #define MAXSPECQUARKS 43 /* std + platform + version + operation */ #define MAXSTDQUARKS 40 #define MAXRHSSIZE 100 /* max supported RHS size */ #define MAXLHSSIZE 200 /* max supported LHS size */ #define MAXINTSTRSIZE 15 /* handle any long int -> string */ typedef enum { __DtXLATE_TYPE_NONE = 0, __DtXLATE_TYPE_INHIBIT = 1, __DtXLATE_TYPE_REGEX = 2, __DtXLATE_TYPE_PURE = 3, __DtXLATE_TYPE_CONTAINS = 4 } __DtXlateType; #if defined(sun) /* Sun doesn't support regcomp() and regexec() yet, so define this here and fill it will the results of advance() */ typedef struct regmatch_t { int rm_so; /* start offset */ int rm_eo; /* end offset */ } regmatch_t; #endif /*========================================================*/ /*====================== Typedefs ========================*/ /*========================================================*/ #if DOC /*========================================================*/ $PTYPEBEG$: __DtXlateDbRec $1LINER$: A private object used to represent translation dbs $SUMMARY$: __DtXlateDbRec is the type of the contents of a translation database object. The database object must be opened before use and closed after use. The definition of the object is opaque to users. $ARGS$: xrmDb: Xrm database used to hold specs initGuard: used to test whether Db initialized /*================================================$SKIP$==*/ #endif /*$DEF$*/ typedef struct __DtXlateDbRec { XrmDatabase xrmDb; int initGuard; Boolean debugMode; } __DtXlateDbRec; /*$END$*/ #if DOC /*========================================================*/ $PTYPEBEG$: __DtXlateSrchData $1LINER$: A private object used to collect search-related data $SUMMARY$: __DtXlateSrchData stores all the data pertaining to a search and the search results. The search routines utilize this to maintain status info over multiple calls by the enumeration routines and to return info to the routine that initiated the search. $ARGS$: filterQuarks: quark list for the platform, version, and op filter stdValueQuarks: quark list for the std value opValue: ptr to operation-specific value string SpecRef: indices 1 to MAXSPECQUARKS point to matching Xrm strings SubEx: indices 1 to MAXSPECQUARKS index sub exprs in opValue /*================================================$SKIP$==*/ #endif /*$DEF$*/ typedef struct __DtXlateSrchData { /* db info */ _DtXlateDb db; /* query info */ const char * platformStr; XrmQuark platformQuark; int version; char verStr[MAXINTSTRSIZE]; /* handle any long int */ const char * operStr; int operStrLen; XrmQuark operQuark; XrmQuark lessThanQuark; XrmQuark equalsToQuark; XrmQuark containsQuark; XrmQuark moreThanQuark; XrmQuark inhibitQuark; /* query or search info */ XrmQuark stdValueQuarks[MAXSTDQUARKS]; const char * opValue; /* search info */ __DtXlateType curTransType; __DtXlateType bestTransType; int curScore; int bestScore; /* MAXSPECQUARKS is depended upon elsewhere to be the size of these */ const char * curSpecRefs[MAXSPECQUARKS]; const char * bestSpecRefs[MAXSPECQUARKS]; regmatch_t curSubEx[MAXSPECQUARKS]; /* pattern match data */ regmatch_t bestSubEx[MAXSPECQUARKS]; /* pattern match data */ } __DtXlateSrchData; /*$END$*/ /*========================================================*/ /*================== Private routines ====================*/ /*========================================================*/ #if DOC /*========================================================*/ $PFUNBEG$: ExpandPath() $1LINER$: adds current working directory to front of path if its relative $SUMMARY$: If path is absolute, returns a malloced copy. If path is relative, inserts the CWD in front of the relative path and returns a mallocedd memory. The caller must free the memory when no longer needed. $ARGS$: filespec: the pathname $RETURNS$: ptr to mallocedd memory or NULL /*================================================$SKIP$==*/ #endif char * ExpandPath ( const char * filespec) { /* $CODE$ */ char tmpPath[MAXPATHLEN + 2]; char * pathName; char * eos = NULL; const char * slash = NULL; /* test args */ if (NULL == filespec) return NULL; /*** is the file absolute ***/ /* if filespec begins with / then it is absolute */ if ( ( MB_CUR_MAX == 1 || mblen(filespec, MB_CUR_MAX) == 1)/* 1st char is 1 byte */ && *filespec == DIR_SLASH) /* and its a / */ { return strdup(filespec); /* RETURN */ } /*** otherwise, make it relative to the current directory ***/ /* get user's current working directory */ if (getcwd(tmpPath, MAXPATHLEN) == 0) return NULL; /* RETURN: error */ /*** add a slash to end of path component, if needed ***/ /* get end of the string */ eos = tmpPath + strlen(tmpPath); /* get last slash */ _DtMBStrrchr(tmpPath,DIR_SLASH,-1,&slash); if (slash != (eos-1)) /* is slash last char of path? */ { *eos++ = DIR_SLASH; *eos = EOS; } /* make a malloc'd copy of the path with room to grow */ pathName = malloc(sizeof(char) * (strlen(filespec) + (eos-tmpPath) + 5) ); /* 5: arbitrary */ if (NULL == pathName) return NULL; /* RETURN: error */ /* build the absolute path */ strcpy(pathName,tmpPath); strcat(pathName,filespec); return pathName; /* RETURN: found */ } /* $END$ */ #if DOC /*========================================================*/ $PFUNBEG$: DeleteDbMem() $1LINER$: Zeros out the db mem & frees it $SUMMARY$: Zeros out the db mem & frees it The xrmDb should have already been destroyed before calling this function. $ARGS$: io_db: db to delete $RETURNS$: /*================================================$SKIP$==*/ #endif static void DeleteDbMem( _DtXlateDb * io_db) { /*$CODE$*/ /* zero out object mem and free it */ (*io_db)->xrmDb = NULL; (*io_db)->initGuard = 0; free(*io_db); *io_db= NULL; } /*$END$*/ #if DOC /*========================================================*/ $IFUNBEG$: _DtMBStrchr() $1LINER$: Searches for a character in a multibyte string $SUMMARY$: Returns in 'ret_ptr' the address of the first occurence of 'value' in string s1. Value may also be the end of string marker '\0'. $ARGS$: $RETURNS$: -1 If found an invalid character. 0 If found value in string s2 1 If found the null byte character without finding 'value'. 'ret_ptr' will also be null in this case. /*================================================$SKIP$==*/ #endif int _DtMBStrchr ( const char * s1, int value, int max_len, const char * * ret_ptr ) { /*$CODE$*/ int len; const char * p1; wchar_t wcs; *ret_ptr = NULL; if (!s1 || *s1 == '\0') return 1; if (max_len == 1) { *ret_ptr = strchr (s1, value); if (*ret_ptr) return 0; return 1; } p1 = s1; while (*p1 != '\0') { len = mblen (p1, max_len); if (len == -1) return -1; if (len == 1) { if (*p1 == value) { *ret_ptr = p1; return 0; } p1++; } else { if (mbstowcs (&wcs, p1, 1) == value) { *ret_ptr = p1; return 0; } p1 += len; } } /* check for match on EOS */ if (*p1 == value) *ret_ptr = p1; return ((*ret_ptr) ? 0 : 1); } /*$CODE$*/ #if DOC /*========================================================*/ $IFUNBEG$: _DtMBStrrchr() $1LINER$: Searches for a character in a multibyte string $SUMMARY$: Returns in 'ret_ptr' the address of the last occurence of 'value' in string s1. Value may also be the end of string marker '\0'. $ARGS$: $RETURNS$: -1 If found an invalid character. 0 If found value in string s2 1 If found the null byte character without finding 'value'. 'ret_ptr' will also be null in this case. /*================================================$SKIP$==*/ #endif int _DtMBStrrchr ( const char * s1, int value, int max_len, const char * * ret_ptr ) { /*$CODE$*/ int len; const char * p1; wchar_t wcs; *ret_ptr = NULL; if (!s1 || *s1 == '\0') return 1; if (max_len == 1) { *ret_ptr = strrchr (s1, value); if (*ret_ptr) return 0; return 1; } p1 = s1; while (*p1 != '\0') { len = mblen (p1, max_len); if (len == -1) return -1; if (len == 1) { if (*p1 == value) *ret_ptr = p1; p1++; } else { if (mbstowcs (&wcs, p1, 1) == value) *ret_ptr = p1; p1 += len; } } /* check for match on EOS */ if (*p1 == value) *ret_ptr = p1; return ((*ret_ptr) ? 0 : 1); } #if DOC /*========================================================*/ $PFUNBEG$: SetDebugModeState() $1LINER$: Checks db for debug mode and sets flag $SUMMARY$: $WARNING$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static void SetDebugModeState( _DtXlateDb dbRec) { /*$CODE$*/ XrmValue value; char * str_type; if (XrmGetResource(dbRec->xrmDb, DEBUG_XLATE_NAME,DEBUG_XLATE_CLASS,&str_type,&value) == True) dbRec->debugMode = True; } #if DOC /*========================================================*/ $PFUNBEG$: ReplaceMatchallSubex() $1LINER$: Replace matchall subexpression refs (e.g. ?1) with values $SUMMARY$: If subexpressions are specified and referenced, the routine assumes that the string pointed to by pStr was allocated using malloc() and can be resized using realloc(). The value and size of pStr may be different after the call. $WARNING$: This routine assumes it is working on a stdvalue expression (e.g. from the LHS of a spec), that uses only stdvalue strings or matchall-style subexpression replacement specs, e.g. ?1. $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static void ReplaceMatchallSubex( char * * pStr, regmatch_t * subex, const char * matchedStr) { /*$CODE$*/ char * nxt = *pStr; char * old = *pStr; /* strip escapes out */ for ( *nxt = *old; /* xfer but don't advance */ *old != EOS; *nxt = *old ) /* xfer but don't advance */ { if ( *old == MATCHALL_CHAR ) /* if an escaped char follows */ { /* if MATCHALL_CHAR is not followed by a digit, e.g. \1 or no replacement values exist, ignore it */ if ( NULL == subex || isdigit(*(old+1)) == 0 ) { old++; /* go past the escape char */ *nxt++ = *old++; /* keep just the char that was escaped and assign here to avoid tranlating that character, then move on to the next one */ continue; /* CONTINUE */ } else /* a value reference is being made */ { /* get the refNum and advance the ptr */ int refNum, numLen; int newOff, oldOff; sscanf(++old,"%d%n", &refNum, &numLen); old += numLen; /* move old ptr past the ref number */ /* printf("%d=%s\n", refNum, &matchedStr[subex[refNum].rm_so]); ** DBG */ /* test for valid replacement */ if ( refNum >= 0 && refNum < MAXSPECQUARKS && subex[refNum].rm_so != -1) { int repLen; int strLen; char * oldTmp; char * newTmp; newOff = nxt - *pStr; oldOff = old - *pStr; repLen = subex[refNum].rm_eo - subex[refNum].rm_so; strLen = strlen(*pStr); /* expand memory and reset pointers */ *pStr = realloc(*pStr,strLen+repLen+1); if (NULL == *pStr) return; /* RETURN */ nxt = *pStr + newOff; old = *pStr + oldOff; /* move rest back to leave room for the replacement value */ oldTmp = *pStr+strLen; /* pts to old EOS */ newTmp = *pStr+strLen-(oldOff-newOff)+repLen;/*pts to new EOS*/ while (oldTmp >= old) *newTmp-- = *oldTmp--; /* replace the ref with a value but don't append EOS */ strncpy(nxt,&matchedStr[subex[refNum].rm_so],repLen); nxt += repLen; /* move new to end of replace string */ old += repLen - (oldOff-newOff); /* move old to end of expanded old string */ } /* if valid replacement */ } /* if a replacement requested */ continue; /* CONTINUE */ } /* if an escaped character */ /* if survived all the checks, can advance to next char */ nxt++; old++; } } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: StripMetaAndReplaceEscSubex() $1LINER$: Strips off meta chars and replaces escaped subex (e.g. \1) values $SUMMARY$: Strip is performed in place if replaceValues is NULL. If replaceValues are specified and referenced, the routine assumes that the string pointed to by pStr was allocated using malloc() and can be resized using realloc(). The value and size of pStr may be different after the call. $WARNING$: This routine assumes it is working on a value expression (e.g. from the RHS of a spec), that uses meta chars and regex(5)-style subexpression replacement specs. $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static void StripMetaAndReplaceEscSubex( char * * pStr, const Boolean keepEscChar, const char * * replaceValues) { /*$CODE$*/ char * nxt = *pStr; char * old = *pStr; Boolean inQuote = False; /* strip escapes out */ for ( *nxt = *old; /* xfer but don't advance */ *old != EOS; *nxt = *old ) /* xfer but don't advance */ { if ( *old == ESCAPE_CHAR ) /* if an escaped char follows */ { /* if ESCAPE_CHAR is not followed by a digit, e.g. \1 or no replacement values exist, ignore it */ if ( NULL == replaceValues || keepEscChar || isdigit(*(old+1)) == 0 ) { if (!keepEscChar) old++; /* go past the escape char */ else *nxt++ = *old++; /* copy esc char over */ *nxt++ = *old++; /* keep just the char that was escaped and assign here to avoid tranlating that character, then move on to the next one */ continue; /* CONTINUE */ } else /* a value reference is being made */ { /* get the refNum and advance the ptr */ int refNum, numLen; int newOff, oldOff; sscanf(++old,"%d%n", &refNum, &numLen); old += numLen; /* move old ptr past the ref number */ /* printf("%x=%s\n", replaceValues[refNum], replaceValues[refNum]); ** DBG */ /* test for valid replacement */ if ( refNum >= 0 && refNum < MAXSPECQUARKS && replaceValues[refNum] != NULL) { int repLen; int strLen; char * oldTmp; char * newTmp; newOff = nxt - *pStr; oldOff = old - *pStr; repLen = strlen(replaceValues[refNum]); strLen = strlen(*pStr); /* expand memory and reset pointers */ *pStr = realloc(*pStr,strlen(*pStr)+repLen+1); if (NULL == *pStr) return; /* RETURN */ nxt = *pStr + newOff; old = *pStr + oldOff; /* move rest back to leave room for the replacement value */ oldTmp = *pStr+strLen; /* pts to old EOS */ newTmp = *pStr+strLen-(oldOff-newOff)+repLen;/*pts to new EOS*/ while (oldTmp >= old) *newTmp-- = *oldTmp--; /* replace the ref with a value but don't append EOS */ strncpy(nxt,replaceValues[refNum],repLen); nxt += repLen; /* move new to end of replace string */ old += repLen - (oldOff-newOff); /* move old to end of expanded old string */ } /* if valid replacement */ } /* if a replacement requested */ continue; /* CONTINUE */ } /* if an escaped character */ else { /* a non-escaped char; make further checks */ if ( *old == COMMENT_CHAR ) { *old = EOS; /* end the string */ continue; /* CONTINUE */ } else if ( *old == QUOTE_CHAR ) { if ( !inQuote) inQuote = True; /* start quote */ else inQuote = False; /* end quote */ old++; /* go to next char */ continue; /* CONTINUE */ } else if ( !inQuote && isspace(*old) ) { old++; /* skip the space */ continue; /* CONTINUE */ } } /* else non-escaped char */ /* if survived all the checks, can advance to next char */ nxt++; old++; } } /*$END$*/ /*========================================================*/ /*============== Private Xlate routines ==================*/ /*========================================================*/ #if DOC /*========================================================*/ $PFUNBEG$: PrintDbEntry() $1LINER$: Prints a db entry to stdout $SUMMARY$: $ARGS$: quarks: NULLQUARK-terminated list of quarks for the LHS of the entry value: value of the RHS of the entry $RETURNS$: /*================================================$SKIP$==*/ #endif static void PrintDbEntry( XrmQuarkList quarks, XrmValue * value) { /*$CODE$*/ char * str; XrmQuark quark; /* print the entry */ quark=*quarks; while ( quark != NULLQUARK ) { str = XrmQuarkToString(quark); fprintf(stderr,"%s", str); quark = *(++quarks); if (quark != NULLQUARK) fprintf(stderr,"."); } fprintf(stderr,":%s\n",value->addr); } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: strCaseiCmp $1LINER$: case insensitive string comparison $SUMMARY$: Rolled my own because strcasecmp() not available on all platforms. $ARGS$: $RETURNS$: True: strings match False: they do not /*================================================$SKIP$==*/ #endif static Boolean strCaseiCmp( const char * str1, const char * str2) { /*$CODE$*/ /* if ( !str1 || !str2 ) return False; *//* unneeded performance hit */ while ( *str1 && *str2 ) if ( tolower(*str1++) != tolower(*str2++) ) return False; return (*str1 == *str2); } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: CheckForMatchall() $1LINER$: Matches search pattern to spec data $SUMMARY$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static Boolean CheckForMatchall( __DtXlateSrchData * srchData, const char * matchallString, const char * matchingString) { /*$CODE$*/ int refNum = -1; int score = 0; /* test for a match all */ if (! ( matchallString[0] == MATCHALL_CHAR && ( matchallString[1] == EOS || sscanf(matchallString+1,"%d", &refNum) == 1) ) ) return False; /* RETURN: syntax error or non-matchall */ /* matchall occurred; save the matching string if valid ref num */ if ( refNum != -1 && refNum >= 0 && refNum < XtNumber(srchData->curSpecRefs) ) { /* Don't store the string if it is just a matchall */ /* This allows a replacement ref to be deleted on a match for which there is no replacement value. */ if ( matchingString[0] == MATCHALL_CHAR && matchingString[1] == EOS ) { srchData->curSpecRefs[refNum] = NULL; /* don't add to score for a matchall with no replacement value */ } else { /* recall: string not owned by curSpecRefs */ srchData->curSpecRefs[refNum] = matchingString; score = 1; } } else /* not a value reference; just determine if a plain match */ { if ( matchingString[0] == MATCHALL_CHAR && matchingString[1] == EOS ) score = 1; /* a plain matchall matches a plain matchall */ } /* if a perfect matchall match, bump the score */ srchData->curScore += score; return True; /* RETURN: matchall */ } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: CheckSearchPlatformMatchesSpec() $1LINER$: Matches search pattern to spec data $SUMMARY$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static Boolean CheckSearchPlatformMatchesSpec( __DtXlateSrchData * srchData, XrmQuark specPlatformQuark) { /*$CODE$*/ /* CheckForMatchall stores away the matching string if of form ?n */ if (srchData->platformQuark != NULLQUARK) { char * specStr = XrmQuarkToString(specPlatformQuark); /* CheckForMatchall incs score if appropriate */ if (CheckForMatchall(srchData,specStr,srchData->platformStr) == True) return True; /* RETURN: platform matches */ } if (srchData->platformQuark == specPlatformQuark) { srchData->curScore += 2; /* perfect match better than matchall match */ return True; /* RETURN: platform matches */ } return False; /* RETURN: platform doesnt match */ } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: CheckSearchVerMatchesSpec() $1LINER$: Matches search pattern to spec data $SUMMARY$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static Boolean CheckSearchVerMatchesSpec( __DtXlateSrchData * srchData, XrmQuark specVersionQuark) { /*$CODE$*/ const char * numStr = XrmQuarkToString(specVersionQuark); int lowerBnd = 0; int upperBnd = INT_MAX; int score = 0; if ( srchData->version == MATCHALL_VER || CheckForMatchall(srchData,numStr,srchData->verStr) == True ) return True; /* RETURN; matchall specified */ /*** sscanf()-based parsing ***/ /* note that the (score=x) is an assignment, not a compare */ if ( (score=1) && sscanf(numStr,"%d-%d", &lowerBnd, &upperBnd) != 2 && (score=1) && sscanf(numStr,"%d+", &lowerBnd) != 1 && (score=2) && sscanf(numStr,"%d", &lowerBnd) != 1 ) return False; /* RETURN: syntax error */ if ( lowerBnd > srchData->version || upperBnd < srchData->version ) return False; /* RETURN: version doesnt match */ srchData->curScore += score; return True; /* RETURN: version matches */ } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: CheckSearchOperMatchesSpec() $1LINER$: Matches search pattern to spec data $SUMMARY$: $ARGS$: srchData: state of the search specOperationQuark: quark for the operation specification string $RETURNS$: True: if srchData->operation is found in specOperation string or the specOperation string is a match all False: if not /*================================================$SKIP$==*/ #endif static Boolean CheckSearchOperMatchesSpec( __DtXlateSrchData * srchData, XrmQuark specOperationQuark) { /*$CODE$*/ const char * opStr = XrmQuarkToString(specOperationQuark); int hitLen; const char * hit; const char * remainingOps; if ( srchData->operStr == NULL || CheckForMatchall(srchData,opStr,srchData->operStr) == True || CheckForMatchall(srchData,srchData->operStr,opStr) == True ) return True; /* RETURN; matchall specified */ /* quark compare search */ if ( specOperationQuark == srchData->operQuark ) goto matched; /*** strstr-based search ***/ hitLen = srchData->operStrLen; remainingOps = opStr; do { /* look for operation in remainingOps */ hit = strstr(remainingOps,srchData->operStr); /* see if the hit is on a complete token */ if ( NULL != hit && (hit == remainingOps || *(hit-1) == OPER_SEPARATOR) && (hit[hitLen] == EOS || hit[hitLen] == OPER_SEPARATOR) ) { matched: srchData->curScore += 2; /*perfect match better than a matchall match*/ return True; /* RETURN: operation matches */ } } while(hit != NULL && *(remainingOps = hit+1) != EOS); return False; /* RETURN: no match on operation */ } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: CheckSearchDirOpToStdMatchesSpec() $1LINER$: Matches search pattern to spec data $SUMMARY$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static Boolean CheckSearchDirOpToStdMatchesSpec( __DtXlateSrchData * srchData, XrmQuark specDirectionQuark) { /*$CODE$*/ int score = 0; __DtXlateType type = __DtXLATE_TYPE_NONE; if (specDirectionQuark == srchData->inhibitQuark) return False; /* RETURN: no match */ /* Note that the type and score expressions are assignments */ if (! ( ( (type=__DtXLATE_TYPE_REGEX) && (specDirectionQuark == srchData->lessThanQuark)) || ( (type=__DtXLATE_TYPE_PURE) && (score=1) && (specDirectionQuark == srchData->equalsToQuark)) || ( (type=__DtXLATE_TYPE_CONTAINS) && (score=1) && (specDirectionQuark == srchData->containsQuark)) ) ) return False; /* RETURN: no match */ srchData->curScore += score; srchData->curTransType = type; return True; /* RETURN: direction matches */ } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: CheckSearchDirStdToOpMatchesSpec() $1LINER$: Matches search pattern to spec data $SUMMARY$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static Boolean CheckSearchDirStdToOpMatchesSpec( __DtXlateSrchData * srchData, XrmQuark specDirectionQuark) { /*$CODE$*/ int score = 0; __DtXlateType type = __DtXLATE_TYPE_NONE; if (specDirectionQuark == srchData->inhibitQuark) return False; /* RETURN: no match */ /* Note that the type and score expressions are assignments */ if (! ( ( (type=__DtXLATE_TYPE_REGEX) && (specDirectionQuark == srchData->moreThanQuark)) || ( (type=__DtXLATE_TYPE_PURE) && (score=1) && (specDirectionQuark == srchData->equalsToQuark)) || ( (type=__DtXLATE_TYPE_CONTAINS) && (score=1) && (specDirectionQuark == srchData->containsQuark)) ) ) return False; /* RETURN: no match */ srchData->curScore += score; srchData->curTransType = type; return True; /* RETURN: direction matches */ } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: CheckSearchStdValueMatchesSpec() $1LINER$: Matches search pattern to spec data $SUMMARY$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static Boolean CheckSearchStdValueMatchesSpec( __DtXlateSrchData * srchData, XrmQuark * specStdValueQuarks) { /*$CODE$*/ int score = 0; int unmatched = 0; XrmQuark * patQuarks; /* walk through all available quarks */ for ( patQuarks = srchData->stdValueQuarks; *specStdValueQuarks != NULLQUARK && *patQuarks != NULLQUARK; specStdValueQuarks++, patQuarks++ ) { char * specStr = XrmQuarkToString(*specStdValueQuarks); char * patStr = XrmQuarkToString(*patQuarks); if ( CheckForMatchall(srchData,specStr,patStr) == True || CheckForMatchall(srchData,patStr,specStr) == True ) continue; /* no score for a matchall */ /* is not exact match, match fails */ /* be case insensitive when comparing standard values */ if ( *patQuarks != *specStdValueQuarks && strCaseiCmp(specStr,patStr) == False ) return False; /* RETURN: no match */ /* one more match--increase score, go to next */ score++; } /* find out how many stdValue fields were left unmatched */ for ( unmatched = 0; *specStdValueQuarks != NULLQUARK; specStdValueQuarks++ ) { unmatched++; } /* Score is combo of the number matched - the number unmatched and not counting the number matchalls that coincided with the search pattern. This technique allows the spec for val1 to be at "better" match than the one for val2, and val2 to be a better match than val3, and val3 to be a better match than val4. .a.std : val1 querypattern = a.std .?.std : val2 querypattern = a.std .?1.std : \\1val3 querypattern = a.std .a.std : val4 querypattern = ?.std .?.std : val5 querypattern = ?.std .?1.std : \\1val6 querypattern = ?.std .a.std.? : val4 querypattern = a.std .?.std.? : val5 querypattern = a.std .?.std.?1 : \\1val5 querypattern = a.std .?1.std.? : \\1val5 querypattern = a.std .?1.std.?2 : \\1\\2val5 querypattern = a.std .a.std.? : val4 querypattern = a.std.b .?.std.? : val5 querypattern = a.std.b .?.std.?1 : \\1val5 querypattern = a.std.b .?1.std.? : \\1val5 querypattern = a.std.b .?1.std.?2 : \\1\\2val5 querypattern = a.std.b */ srchData->curScore += score + MAXSTDQUARKS - unmatched; return True; /* RETURN: direction matches */ } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: CheckSearchOpValueMatchesSpec() $1LINER$: Matches search pattern to spec data $SUMMARY$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static Boolean CheckSearchOpValueMatchesSpec( __DtXlateSrchData * srchData, const char * specOpValue) { /*$CODE$*/ char opValBuf[MAXRHSSIZE]; /* max supported RHS size */ char * pOpValBuf = opValBuf; /* need this for StripMeta... call */ size_t opValLen; Boolean matches = False; /* copy value to mutable memory */ strncpy(opValBuf,specOpValue,sizeof(opValBuf)); opValBuf[sizeof(opValBuf)-1] = EOS; opValLen = strlen(opValBuf); /* depending on the translation type of the spec, do a regexex match of the spec value pattern to the search value or do a pure match */ if (srchData->curTransType == __DtXLATE_TYPE_REGEX) { #if defined(sun) char * ex = NULL; /* True: leave escape char in place */ StripMetaAndReplaceEscSubex(&pOpValBuf,True,NULL); /* we need to use regexex to pattern match */ /* and we need to save of the reference value matches */ if ( (ex = compile(opValBuf,NULL,NULL)) != NULL && advance(srchData->opValue,ex) != 0) { int matchSize; int subExCnt = nbra; /* Sun global for advance() */ matches = True; /* if got this far */ /* need due to bug in advance()--operation doesn't meet documentation */ if (NULL == loc1) loc1=(char *)srchData->opValue; /* inc score by the size of the match after scaling for the maximum possible match size */ matchSize = loc2 - loc1; /*loc[12] are Sun globals for advance()*/ if (matchSize < 0 || matchSize >= sizeof(opValBuf)) matchSize = 0; /* NOTE: this scoring code should be identical in the Sun-specific and non-Sun code blocks */ if (matchSize == strlen(srchData->opValue)) { /* if the matchSize is the length of srchData->opValue, then we have a complete match. In this case, use the specificity of the pattern to pick the best match */ /* NOTE: opValLen is a crude measure of specificity. A better measure would be to count the number of literals/ranges that matched exactly. When doing this, a perfect match without regex syntax should rank higher than a perfect match with regex syntax. This is one area where the current algorithm breaks. For example: opValue=23, pat1=23, pat2=[0-9]3. Both patterns match and pat1 is a better match, but not with the current length-based algorithm. */ /* NOTE: this formula does not advance the score to sizeof(opValBuf) for a perfect match. Other match formulas use sizeof(opValBuf) as the max value to indicate a perfect match. */ srchData->curScore += matchSize + opValLen; } else { /* if its not a complete match, inc score by match size */ srchData->curScore += matchSize; } /* put sub expression matching stuff in srchData->curSubEx */ for( ; nbra > 0; nbra-- ) { srchData->curSubEx[nbra].rm_so = braslist[nbra-1] - loc1; srchData->curSubEx[nbra].rm_eo = braelist[nbra-1] - loc1; } } if (ex) free(ex); #else regex_t re; /* True: leave escape char in place */ StripMetaAndReplaceEscSubex(&pOpValBuf,True,NULL); /* we need to use regexex to pattern match */ /* and we need to save of the reference value matches */ if ( regcomp(&re,opValBuf,0) == 0 && regexec(&re,srchData->opValue, XtNumber(srchData->curSubEx),srchData->curSubEx,0) == 0) { int matchSize; matches = True; /* if got this far */ /* inc score by the size of the match after scaling for the maximum possible match size */ matchSize = srchData->curSubEx[0].rm_eo - srchData->curSubEx[0].rm_so; /* NOTE: this scoring code should be identical in the Sun-specific and non-Sun code blocks */ if (matchSize == strlen(srchData->opValue)) { /* if the matchSize is the length of srchData->opValue, then we have a complete match. In this case, use the specificity of the pattern to pick the best match */ /* NOTE: opValLen is a crude measure of specificity. A better measure would be to count the number of literals/ranges that matched exactly. When doing this, a perfect match without regex syntax should rank higher than a perfect match with regex syntax. This is one area where the current algorithm breaks. For example: opValue=23, pat1=23, pat2=[0-9]3. Both patterns match and pat1 is a better match, but not with the current length-based algorithm. */ /* NOTE: this formula does not advance the score to sizeof(opValBuf) for a perfect match. Other match formulas use sizeof(opValBuf) as the max value to indicate a perfect match. */ srchData->curScore += matchSize + opValLen; } else { /* if its not a complete match, inc score by match size */ srchData->curScore += matchSize; } /* sub expression matching stuff already in srchData->curSubEx */ } regfree(&re); #endif } else /* (srchData->curTransType == __DtXLATE_TYPE_PURE || __DtXLATE_TYPE_CONTAINS */ { char * opValueInBuf; /* False: strip escape char as well */ StripMetaAndReplaceEscSubex(&pOpValBuf,False,NULL); matches = (strcmp(srchData->opValue,opValBuf) == 0); /* if matches, inc score to show a perfect match (max poss value) */ if (matches) srchData->curScore += sizeof(opValBuf); /* don't test for contains if a perfect match or a pure match spec */ if (matches || srchData->curTransType == __DtXLATE_TYPE_PURE) return matches; /* RETURN */ /* (srchData->curTransType == __DtXLATE_TYPE_CONTAINS) */ /* is opValue contained in opValBuf? */ /* is opValBuf contained in opValue? */ opValueInBuf = NULL; matches = (opValBuf[0] != EOS && srchData->opValue[0] != EOS) && ((opValueInBuf=strstr(opValBuf,srchData->opValue)) != NULL || strstr(srchData->opValue,opValBuf) != NULL); /* if matches, inc score to show a contains match */ if (matches) { if (opValueInBuf) srchData->curScore += strlen(srchData->opValue); else srchData->curScore += strlen(opValBuf); } } return matches; } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: FindStdToOpMatchCB() $1LINER$: Matches std value of entry to search pattern; gets op value $SUMMARY$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static Bool FindStdToOpMatchCB( XrmDatabase * database, XrmBindingList bindings, XrmQuarkList quarks, XrmRepresentation * type, XrmValue * value, XPointer client_data) { /*$CODE$*/ __DtXlateSrchData * srchData = (__DtXlateSrchData *) client_data; /* always begin scoring from 0 and replacement values at NULL */ srchData->curScore = 0; memset(srchData->curSpecRefs,0, sizeof(srchData->curSpecRefs)); #ifdef DBG_MATCHING fprintf(stderr,"FindStdToOpMatch: "); PrintDbEntry(quarks,value); #endif /* check for a match */ if ( CheckSearchPlatformMatchesSpec(srchData, quarks[PLATFORM_QUARK]) == False) return False; /* continue enumeration */ #ifdef DBG_MATCHING fprintf(stderr,"platform matches\n"); #endif if ( CheckSearchVerMatchesSpec(srchData, quarks[VERSION_QUARK]) == False) return False; /* continue enumeration */ #ifdef DBG_MATCHING fprintf(stderr,"ver matches\n"); #endif if ( CheckSearchOperMatchesSpec(srchData, quarks[OPERS_QUARK]) == False) return False; /* continue enumeration */ #ifdef DBG_MATCHING fprintf(stderr,"oper matches\n"); #endif if ( CheckSearchDirStdToOpMatchesSpec(srchData, quarks[DIRECTION_QUARK]) == False) return False; /* continue enumeration */ #ifdef DBG_MATCHING fprintf(stderr,"kind matches\n"); #endif /* now check for std value match and, if it is the best match so far, record the value */ if ( CheckSearchStdValueMatchesSpec(srchData, &quarks[FIRSTSTD_QUARK]) == False ) { if (srchData->db->debugMode) { fprintf(stderr,"mismatch "); PrintDbEntry(quarks,value); } return False; /* continue enumeration */ } #ifdef DBG_MATCHING fprintf(stderr,"std value matches\n"); #endif if (srchData->db->debugMode) { fprintf(stderr,"match (%d) ",srchData->curScore); PrintDbEntry(quarks,value); } /* we have a match! (we made it through all match checks) */ /* is it better than or same as any earlier match? */ if ( srchData->curScore >= srchData->bestScore ) { /* recall that all strings are owned by Xrm==>no need to free them */ srchData->bestScore = srchData->curScore; srchData->bestTransType = srchData->curTransType; memcpy(srchData->bestSpecRefs,srchData->curSpecRefs, sizeof(srchData->bestSpecRefs)); /* no array assignment in C */ srchData->opValue = value->addr; } return False; /* continue enumeration */ } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: FindOpToStdMatchCB() $1LINER$: Matches op value of entry to search pattern; gets std value $SUMMARY$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static Bool FindOpToStdMatchCB( XrmDatabase * database, XrmBindingList bindings, XrmQuarkList quarks, XrmRepresentation * type, XrmValue * value, XPointer client_data) { /*$CODE$*/ __DtXlateSrchData * srchData = (__DtXlateSrchData *) client_data; /* always begin scoring from 0 and subexpression indices at -1 */ srchData->curScore = 0; memset(srchData->curSubEx,-1, sizeof(srchData->curSubEx)); #ifdef DBG_MATCHING fprintf(stderr,"FindOpToStdMatch: "); PrintDbEntry(quarks,value); #endif /* check for a match */ if ( CheckSearchPlatformMatchesSpec(srchData, quarks[PLATFORM_QUARK]) == False) return False; /* continue enumeration */ #ifdef DBG_MATCHING fprintf(stderr,"platform matches\n"); #endif if ( CheckSearchVerMatchesSpec(srchData, quarks[VERSION_QUARK]) == False) return False; /* continue enumeration */ #ifdef DBG_MATCHING fprintf(stderr,"ver matches\n"); #endif if ( CheckSearchOperMatchesSpec(srchData, quarks[OPERS_QUARK]) == False) return False; /* continue enumeration */ #ifdef DBG_MATCHING fprintf(stderr,"oper matches\n"); #endif if ( CheckSearchDirOpToStdMatchesSpec(srchData, quarks[DIRECTION_QUARK]) == False) return False; /* continue enumeration */ #ifdef DBG_MATCHING fprintf(stderr,"kind matches\n"); #endif /* now check for op value match and, if it is the best match so far, record the std value */ if ( CheckSearchOpValueMatchesSpec(srchData, value->addr) == False ) { if (srchData->db->debugMode) { fprintf(stderr,"mismatch "); PrintDbEntry(quarks,value); } return False; /* continue enumeration */ } #ifdef DBG_MATCHING fprintf(stderr,"op value matches\n"); #endif if (srchData->db->debugMode) { fprintf(stderr,"match (%d) ",srchData->curScore); PrintDbEntry(quarks,value); } /* we have a match! (we made it through all match checks) */ /* is it better than or same as any earlier match? */ if ( srchData->curScore >= srchData->bestScore ) { XrmQuarkList stdQ; XrmQuarkList curQ; /* recall that all strings are owned by Xrm==>no need to free them */ srchData->bestScore = srchData->curScore; srchData->bestTransType = srchData->curTransType; memcpy(srchData->bestSubEx,srchData->curSubEx, sizeof(srchData->bestSubEx)); /* no array assignment in C */ /* store off the std value of the best match */ stdQ = srchData->stdValueQuarks; curQ = &quarks[FIRSTSTD_QUARK]; while ( (*stdQ = *curQ) != NULLQUARK ) stdQ++, curQ++; } return False; /* continue enumeration */ } /*$END$*/ #if DOC /*========================================================*/ $PFUNBEG$: DoCommonSrchDataPrep $1LINER$: Prep srchData to search for a pattern $SUMMARY$: $ARGS$: $RETURNS$: /*================================================$SKIP$==*/ #endif static void DoCommonSrchDataPrep( __DtXlateSrchData * srchData, _DtXlateDb db, const char * platform, const int version, const char * operation) { /*$CODE$*/ int verNum = version; /* for lint */ /* zero the search data */ memset(srchData,0,sizeof(__DtXlateSrchData)); /* set the db */ srchData->db = db; /* build filter list for enumerating the db */ if (verNum < MATCHALL_VER) verNum = MATCHALL_VER; srchData->platformStr = platform; srchData->platformQuark = (platform?XrmStringToQuark(platform):NULLQUARK); srchData->version = verNum; sprintf(srchData->verStr,"%d",verNum); srchData->operStr = operation; srchData->operStrLen = strlen(operation); srchData->operQuark = (operation ? XrmStringToQuark(operation) : NULLQUARK); srchData->lessThanQuark = XrmStringToQuark(LESSTHAN_STR); srchData->equalsToQuark = XrmStringToQuark(EQUALS_STR); srchData->containsQuark = XrmStringToQuark(CONTAINS_STR); srchData->moreThanQuark = XrmStringToQuark(MORETHAN_STR); srchData->inhibitQuark = XrmStringToQuark(INHIBIT_STR); } /*$END$*/ /*========================================================*/ /*=============== Public Xlate routines ==================*/ /*========================================================*/ #if DOC /*========================================================*/ $FUNBEG$: _DtXlateOpenDb() $1LINER$: Open a translation database $SUMMARY$: Opens a translation resource database and returns a reference to it in ret_db. Initializes the _DtXlateDb object to ready for use. If an error occurs, _DtXlateDb is set to NULL. $ARGS$: $RETURNS$: 0: no error occurred -1: if ret_db is NULL if XrmGetFileDatabase() failed on databaseName if malloc fails to alloc a db structure /*================================================$SKIP$==*/ #endif int _DtXlateOpenDb( const char * databaseName, _DtXlateDb * ret_db) { /*$CODE$*/ XrmDatabase xrmDb; __DtXlateDbRec * dbRec = NULL; char * path = NULL; if(NULL == ret_db) return -1; /* RETURN */ *ret_db = NULL; /* Do NOT check for whether *ret_db is already a valid db; this is none of our affair. */ /* get an absolute path for the file */ path = ExpandPath(databaseName); if (NULL == path) return -1; /* RETURN */ xrmDb = XrmGetFileDatabase(path); if (NULL == xrmDb) { free(path); return -1; } /* RETURN */ /* alloc a db ref */ dbRec = (__DtXlateDbRec *) calloc(1,sizeof(__DtXlateDbRec)); if(NULL == dbRec) { XrmDestroyDatabase(xrmDb); free(path); return -1; /* RETURN */ } /* and populate it */ dbRec->xrmDb = xrmDb; dbRec->initGuard = INIT_OCCURRED; /* check for debug mode */ SetDebugModeState(dbRec); if (dbRec->debugMode) fprintf(stderr,"_DtXlateOpenDb: opened: %s; new db: %p\n",path, (void *)dbRec); *ret_db = dbRec; free(path); return 0; /* RETURN */ } /*$END$*/ #if DOC /*========================================================*/ $FUNBEG$: _DtXlateOpenAndMergeDbs() $1LINER$: Opens a translation database and merges with earlier dbs $SUMMARY$: Opens a translation resource database and returns a reference to it in ret_db. Initializes the _DtXlateDb object to ready for use. $ARGS$: $RETURNS$: 0: no error occurred -1: if io_db is NULL if XrmGetFileDatabase() failed on databaseName /*================================================$SKIP$==*/ #endif int _DtXlateOpenAndMergeDbs( const char * databaseName, _DtXlateDb * io_db) { /*$CODE$*/ XrmDatabase xrmDb; char * path; int ret; if(NULL == io_db) return -1; /* RETURN */ /* if a db has not yet been opened */ if( NULL == *io_db || (*io_db)->initGuard != INIT_OCCURRED || (*io_db)->xrmDb == NULL) { ret = _DtXlateOpenDb(databaseName,io_db); /* RETURN */ if ( (*io_db) && (*io_db)->debugMode) fprintf(stderr,"_DtXlateOpenAndMergeDb: " "used _DtXlateOpenDb to open first file\n"); return ret; /* RETURN */ } if ( (*io_db) && (*io_db)->debugMode) fprintf(stderr,"_DtXlateOpenAndMergeDb: " "target file: %s; existing db: %p\n",databaseName, (void *) *io_db); /* a db has been opened, let's merge with it */ /* get an absolute path for the file */ path = ExpandPath(databaseName); if (NULL == path) goto Failed; /* RETURN */ xrmDb = XrmGetFileDatabase(path); if (NULL == xrmDb) goto Failed; /* RETURN */ /* merge and destroy xrmDb for me */ XrmMergeDatabases(xrmDb,&(*io_db)->xrmDb); /* check for debug mode */ SetDebugModeState(*io_db); if ((*io_db)->debugMode) fprintf(stderr,"_DtXlateOpenAndMergeDb: " "opened: %s; merged db: %p\n",path, (void *) *io_db); free(path); return 0; /* RETURN */ Failed: if ( (*io_db) && (*io_db)->debugMode) fprintf(stderr,"_DtXlateOpenAndMergeDb: open failed; file: %s\n", (path ? path : (databaseName ? databaseName : "NULL") ) ); if (path) free(path); return -1; /* RETURN */ } /*$END$*/ #if DOC /*========================================================*/ $FUNBEG$: _DtXlateMergeDbs() $1LINER$: Merges two open dbs into one and closes the merged-in db. $SUMMARY$: Merges two databases into one and closes the merged db. The io_dbToMerge database must be a valid translation database. The io_dbToMerge database is merged into the io_mergeIntoDb. The io_mergeIntoDb may either be invalid or valid. If invalid, the io_dbToMerge database is simply moved over to io_mergeIntoDb. If io_mergeIntoDb is valid, the entries in the io_dbToMerge database are merged into it and take precedence over entries in the io_mergeIntoDb, and the io_dbToMerge database is closed. $ARGS$: io_dbToMerge: database to merge into io_mergeIntoDb io_mergeIntoDb: database to hold merged result $RETURNS$: 0: no error occurred -1: if io_dbToMerge or io_mergeIntoDb is NULL if *io_dbToMerge is NULL or uninitialized /*================================================$SKIP$==*/ #endif int _DtXlateMergeDbs( _DtXlateDb * io_dbToMerge, _DtXlateDb * io_mergeIntoDb) { /*$CODE$*/ /* check args */ if( NULL == io_mergeIntoDb || NULL == io_dbToMerge || NULL == *io_dbToMerge || (*io_dbToMerge)->initGuard != INIT_OCCURRED || (*io_dbToMerge)->xrmDb == NULL) return -1; /* RETURN */ /* check for debug mode */ if ( ((*io_mergeIntoDb) && (*io_mergeIntoDb)->debugMode) || (*io_dbToMerge)->debugMode) fprintf(stderr,"_DtXlateMergeDbs: " "mergeIntoDb: %p; dbToMerge: %p\n", (void *) *io_mergeIntoDb, (void *) *io_dbToMerge); /* if db_mergeIntoDb has not yet been opened */ if( NULL == *io_mergeIntoDb || (*io_mergeIntoDb)->initGuard != INIT_OCCURRED || (*io_mergeIntoDb)->xrmDb == NULL) { /* just move dbToMerge into mergeIntoDb */ *io_mergeIntoDb = *io_dbToMerge; DeleteDbMem(io_dbToMerge); return 0; /* RETURN */ } /* merge and destroy io_dbToMerge->xrmDb for me */ XrmMergeDatabases((*io_dbToMerge)->xrmDb,&(*io_mergeIntoDb)->xrmDb); DeleteDbMem(io_dbToMerge); /* check for debug mode */ SetDebugModeState(*io_mergeIntoDb); if ((*io_mergeIntoDb)->debugMode) fprintf(stderr,"merged db: %p\n", (void *) *io_mergeIntoDb); return 0; } /*$END$*/ #if DOC /*========================================================*/ $FUNBEG$: _DtXlateOpenAllDbs() $1LINER$: Open and merge all locale translation databases that can be found $SUMMARY$: DtXlateOpenAllDbs() locates all translation databases present in the search paths directories. $ARGS$: searchPaths: ':' separated list of directories databaseName: name of the database file in those directories ret_db: the reference to the open database is stored here $RETURNS$: 0: at least one database was opened -1: no database was opened /*================================================$SKIP$==*/ #endif int _DtXlateOpenAllDbs( const char * searchPaths, const char * databaseName, _DtXlateDb * ret_db) { /*$CODE$*/ const char * workStart = searchPaths; const char * separator = NULL; char dbFile[MAXPATHLEN+1]; int ret = ~0; /* all bits set */ /* cycle through the paths, opening each one */ do { int workLen = 0; const char * slash = NULL; dbFile[0] = EOS; /* isolate the next part of the path */ _DtMBStrchr (workStart, PATH_SEPARATOR, -1, &separator); if (NULL == separator) _DtMBStrchr (workStart, EOS, -1, &separator); if (NULL == separator) break; /* BREAK */ workLen = separator - workStart; /* don't include +1 for EOS */ /* copy over the path component */ strncpy(dbFile,workStart,workLen); workStart = separator + 1; /* add a slash to end of path component, if needed */ *(dbFile+workLen) = EOS; /* add an EOS for _DtMBStrrchr to find */ _DtMBStrrchr(dbFile,DIR_SLASH,-1,&slash); if (slash != dbFile+workLen-1) /* is slash last char of path? */ { *(dbFile+workLen) = DIR_SLASH; workLen++; } /* append the filename and EOS */ strcpy(dbFile+workLen,databaseName); /*printf("Working on: %s\n", dbFile); **DBG*/ /* open and merge the database with previously opened dbs */ /* by ANDing, we determine whether at least one call returned 0 */ ret &= _DtXlateOpenAndMergeDbs(dbFile,ret_db); } while ( *separator != EOS ); if (*ret_db && (*ret_db)->debugMode) fprintf(stderr,"_DtXlateOpenAllDbs: completed\n" " srchpaths: %s; db file: %s\n",searchPaths,databaseName); return (ret == 0 ? 0 : -1); /* ret != 0 ==> no db was opened */ } /*$END$*/ #if DOC /*========================================================*/ $FUNBEG$: _DtXlateCloseDb() $1LINER$: Close an open translation database $SUMMARY$: _DtXlafteCloseDb() releases all memory associated with the translation database. Further use of the database object is an error. $ARGS$: $RETURNS$: 0: database was valid and has been closed -1: invalid database pointer /*================================================$SKIP$==*/ #endif int _DtXlateCloseDb( _DtXlateDb * io_db) { /*$CODE$*/ __DtXlateDbRec * dbRec; if( NULL == io_db || NULL == (dbRec = *io_db) /* dbRec assigned */ || dbRec->initGuard != INIT_OCCURRED) return -1; /* RETURN */ XrmDestroyDatabase(dbRec->xrmDb); if (dbRec->debugMode) fprintf(stderr,"_DtXlateCloseDb: %p\n", (void *) dbRec); /* zero out object mem and free it */ DeleteDbMem(io_db); return 0; } /*$END$*/ #if DOC /*========================================================*/ $FUNBEG$: _DbXlateStdToOpValue() $1LINER$: Translates a standardized spec to an operation-specific value $SUMMARY$: Looks up the best translation of the standard value for an operation and places a pointer to the translation string at the location pointed to by ret_opValue. The translated string was allocated using malloc() and must be freed when no longer needed. If ret_opValue is NULL, the function merely verifies that a valid translation exists. $ARGS$: db: a translation database platform: the platform string (see _DtXlateGetXlateEnv()) version: the version number (see _DtXlateGetXlateEnv()) operation: the operation of interest, e.g. "setlocale" stdValue: the standard value pattern ret_opValue: location where ptr to translated string is stored ret_reserved: reserved for future use $RETURNS$: 0: translation found -1: invalid database (NULL ptr, not opened) no operation was specified query failed to find a match /*================================================$SKIP$==*/ #endif int _DtXlateStdToOpValue( _DtXlateDb db, const char * platform, const int version, const char * operation, const char * stdValue, char * * ret_opValue, void * ret_reserved) { /*$CODE$*/ __DtXlateSrchData srchData; XrmQuark empty = NULLQUARK; if ( NULL == db || db->initGuard != INIT_OCCURRED || NULL == operation || operation[0] == EOS) return -1; /* RETURN error */ /* prep srch data for search */ DoCommonSrchDataPrep(&srchData,db,platform,version,operation); /* handle a rare case */ if (NULL == stdValue) { if (db->debugMode) fprintf(stderr,"_DtXlateStdToOpValue: NULL std value\n"); if (ret_opValue) *ret_opValue = NULL; return -1; /* RETURN error */ } /* build std value list for use during comparison */ srchData.stdValueQuarks[0] = NULLQUARK; if (NULL != stdValue && stdValue[0] != EOS) XrmStringToQuarkList(stdValue,srchData.stdValueQuarks); if (db->debugMode) fprintf(stderr,"_DtXlateStdToOpValue: %s.%d.%s.%s: \n", platform,version,operation,stdValue); /* scan through this Db looking for matches and put in search */ XrmEnumerateDatabase(db->xrmDb,&empty,&empty, XrmEnumAllLevels, FindStdToOpMatchCB, (XPointer) &srchData); if ( srchData.opValue != NULL && srchData.bestTransType != __DtXLATE_TYPE_INHIBIT ) { char * opValue; if (ret_opValue == NULL) { if (db->debugMode) fprintf(stderr,"translation exists\n"); return 0; /* RETURN: translation exists */ } /* alloc the string to return */ opValue = strdup(srchData.opValue); if (db->debugMode) fprintf(stderr,"raw opval:%s\n",opValue); /* do quote and escape removal and ref replacement in the opValue */ if ( srchData.bestTransType == __DtXLATE_TYPE_REGEX ) StripMetaAndReplaceEscSubex(&opValue,False,srchData.bestSpecRefs); else StripMetaAndReplaceEscSubex(&opValue,False,NULL); if (db->debugMode) fprintf(stderr,"op value:%s\n",opValue); *ret_opValue = opValue; return 0; /* RETURN: search successful */ } return -1; /* RETURN: search failed */ } /*$END$*/ #if DOC /*========================================================*/ $FUNBEG$: _DbXlateOpToStdValue() $1LINER$: Translates an operation-specific value to a standardized one $SUMMARY$: Looks up the best translation of the operation value for an operation and places a pointer to the standard string at the location pointed to by ret_stdValue. The standard string was allocated using malloc() and must be freed when no longer needed. If ret_stdValue is NULL, the function merely verifies that a valid translation exists. $ARGS$: db: a translation database platform: the platform string (see _DtXlateGetXlateEnv()) version: the version number (see _DtXlateGetXlateEnv()) operation: the operation of interest, e.g. "setlocale" opValue: the operation-specific value pattern ret_stdValue: location where ptr to standard string is stored ret_reserved: reserved for future use $RETURNS$: 0: translation found -1: invalid database (NULL ptr, not opened) no operation was specified query failed to find a match /*================================================$SKIP$==*/ #endif int _DtXlateOpToStdValue( _DtXlateDb db, const char * platform, const int version, const char * operation, const char * opValue, char * * ret_stdValue, void * ret_reserved) { /*$CODE$*/ __DtXlateSrchData srchData; XrmQuark empty = NULLQUARK; char lhs[MAXLHSSIZE]; if ( NULL == db || db->initGuard != INIT_OCCURRED || NULL == operation || operation[0] == EOS) return -1; /* RETURN error */ /* prep srch data for search */ DoCommonSrchDataPrep(&srchData,db,platform,version,operation); /* after check on value, store op value for use during comparison */ /* not meaningful to check for a NULL value */ if (NULL == opValue) { if (db->debugMode) fprintf(stderr,"_DtXlateOpToStdValue: NULL op value\n"); if (ret_stdValue) *ret_stdValue = NULL; return -1; /* RETURN error */ } srchData.opValue = opValue; if (db->debugMode) fprintf(stderr,"_DtXlateOpToStdValue: %s.%d.%s.: %s\n", platform,version,operation,opValue); /* scan through this Db looking for matches and put in search */ XrmEnumerateDatabase(db->xrmDb,&empty,&empty, XrmEnumAllLevels, FindOpToStdMatchCB, (XPointer) &srchData); if ( srchData.stdValueQuarks[0] != NULLQUARK && srchData.bestTransType != __DtXLATE_TYPE_INHIBIT ) { XrmQuarkList stdQ; char * stdValue = lhs; int stdValueLen = 0; if (ret_stdValue == NULL) { if (db->debugMode) fprintf(stderr,"translation exists\n"); return 0; /* RETURN: translation exists */ } /* make a STDVALUE_SEPARATOR separated string out of the std data */ for ( stdQ = srchData.stdValueQuarks; *stdQ != NULLQUARK; stdQ++ ) { const char * str = XrmQuarkToString(*stdQ); int strLen; if (NULL == str || str[0] == EOS) continue; strLen = strlen(str); if (stdValue != lhs) {*stdValue++ = STDVALUE_SEPARATOR; stdValueLen++; } if ((stdValueLen + strLen) > sizeof(lhs) ) strLen = sizeof(lhs)-stdValueLen; strncpy(stdValue,str,strLen); stdValueLen += strLen; stdValue += strLen; } *stdValue = EOS; if (db->debugMode) fprintf(stderr,"raw stdval:%s\n",lhs); /* do quote and escape removal and ref replacement in the stdValue */ stdValue = strdup(lhs); /* reset stdValue ptr */ if ( srchData.bestTransType == __DtXLATE_TYPE_REGEX ) ReplaceMatchallSubex(&stdValue,srchData.bestSubEx,srchData.opValue); if (db->debugMode) fprintf(stderr,"std value:%s\n",stdValue); *ret_stdValue = stdValue; return 0; /* RETURN: search successful */ } return -1; /* RETURN: search failed */ } /*$END$*/ #if DOC /*========================================================*/ $FUNBEG$: _DtXlateGetXlateEnv() $1LINER$: Get the DtXlate compilation and execution environments. $SUMMARY$: _DtXlateGetXlateEnv() recovers the identity of the application current platform, the version value of the application execution environment, and the version value of the operating system version for which DtXlateGetXlateEnv() was compiled. These values can be used in formulating queries, especially for the _DtXlateStdToOpValue() query. The technique used by this routine is as follows. Using uname(2), the routine retrieves the sysname, release, and version strings. The sysname is used as the platform name. The release and version strings are concatenated in that order and as treated below as the . An OpToStd translation looks for a match to .?.version.<.: or .?.version.=.: If no match is found, the next fallback position is to get the specification with the same sysname and the highest std-version integer value. If no specifications exist for that sysname, then sysname is set to the empty string and std-version is -1. ret_AppExecEnvPlatform should point to a character array at least _DtPLATFORM_MAX_LEN characters long. The sysname is copied to it. ret_AppExecEnvVersion is given the integer value of the std-version recovered from the translation. ret_XlateCompiledForOSVersion is given the integer value determined by using the OSMajorVersion and OSMinorVersion constants defined by the build environment of _DtXlate as follows: #define _STR(V) #V #define STR(V) _STR(V) sprintf(buf,"%s%s%s", STR(OSMAJORVERSION), nl_langinfo(RADIXCHAR), STR(OSMAJORVERSION)); verNum = (int) (100.0 * atof(buf)); For example: OSMAJORVERSION & OSMINORVERSION are compile-time constants that must be defined as part of the build environment. It is assumed that these constants are of the form: e.g. Sun 5.3 #define OSMAJORVERSION 5 #define OSMINORVERSION 3 530 = (int) 100.0 * atof("5.3"); e.g. HP-UX 8.09 #define OSMAJORVERSION 8 #define OSMINORVERSION 09 809 = (int) 100.0 * atof("8.09"); Note that it may be necessary for the application to determine the version number of an operation in some platform-specific and operation-specific manner, for example using a library version value. In many cases, however, the O.S. version value for which _DtXlate was compiled will be sufficient when identifying version numbers for standard development environment libraries, such as libc. $EXAMPLE$: char * xlateStd2Op(_DtXlateDb db,Boolean runtimeOp, char * operation,char * stdVal) { char platform[_DtPLATFORM_MAX_LEN]; int version; char * opVal = NULL; int ret; if (runtimeOp) ret=_DtXlateGetXlateEnv(db,platform,&version,NULL); else ret=_DtXlateGetXlateEnv(db,platform,NULL,&version); if (ret == 0) _DtXlateStdToOpValue(db,platform,version,operation,stdVal,&opVal,NULL); return opVal; /* will be NULL if error occurred */ } $ARGS$: ret_AppExecEnvPlatform: pts to a string at least _DtPLATFORM_MAX_LEN long that will hold the string uname(2) returns for sysname ret_AppExecEnvVersion: pts to an integer that will receive the platform standardized version number, as determined by a translation on uname(2) release+version. ret_XlateCompiledForOSVersion: pts to an integer that will receive the operating system version for which _DtXlate was compiled using OSMajorVersion * 100 + OSMinorVersion Any of the arguments may be NULL. $RETURNS$: 0: if no error occurred -1: if no translation was possible to get the AppExecEnvVersion /*================================================$SKIP$==*/ #endif int _DtXlateGetXlateEnv( _DtXlateDb db, char * ret_AppExecEnvPlatform, int * ret_AppExecEnvVersion, int * ret_XlateCompiledForOSVersion) { /*$CODE$*/ struct utsname names; int ret = 0; char * platform = "NULL"; int execver = -1; int compver = -1; /* get host specifics */ uname(&names); /* first get execution host name */ if (ret_AppExecEnvPlatform) { strncpy(ret_AppExecEnvPlatform,names.sysname,_DtPLATFORM_MAX_LEN-1); ret_AppExecEnvPlatform[_DtPLATFORM_MAX_LEN-1] = EOS; platform=ret_AppExecEnvPlatform; } /* then look up version number of execution host */ if (ret_AppExecEnvVersion) { char version[sizeof(names.release)+sizeof(names.version)-1]; char * stdVer = NULL; int verNum = MATCHALL_VER; /* cat release version and do a translation on it to a std value */ /* then convert the std value to a integer */ strncpy(version,names.release,sizeof(names.release)-1); version[sizeof(names.release)-1] = EOS; strncat(version,names.version,sizeof(names.version)-1); ret = _DtXlateOpToStdValue(db,names.sysname,0, _DtXLATE_OPER_VERSION,version,&stdVer,NULL); if (ret == 0) { if (sscanf(stdVer,"%d",&verNum) != 1) verNum = MATCHALL_VER;; free(stdVer); } *ret_AppExecEnvVersion = verNum; execver = verNum; } /* then look up version number of execution host */ if (ret_XlateCompiledForOSVersion) { char buf[MAXINTSTRSIZE]; #define _STR(V) #V #define STR(V) _STR(V) /*=========================== OSMAJORVERSION & OSMINORVERSION are compile-time constants that must be defined as part of the build environment. It is assumed that these constants are of the form: e.g. Sun 5.3 #define OSMAJORVERSION 5 #define OSMINORVERSION 3 530 = (int) (100.0 * atof("5.3")); e.g. HP-UX 8.09 #define OSMAJORVERSION 8 #define OSMINORVERSION 09 809 = (int) (100.0 * atof("8.09")); ===========================*/ #if !defined(OSMAJORVERSION) && !defined(OSMINORVERSION) #warning "OSMAJORVERSION and OSMINORVERSION not defined, assuming 99.0: #define OSMAJORVERSION 99 #define OSMINORVERSION 0 #endif #if defined(__linux__) || defined(CSRG_BASED) sprintf(buf,"%s%s%s", STR(OSMAJORVERSION), nl_langinfo('.'), STR(OSMINORVERSION)); #else sprintf(buf,"%s%s%s", STR(OSMAJORVERSION), nl_langinfo(RADIXCHAR), STR(OSMINORVERSION)); #endif *ret_XlateCompiledForOSVersion = (int) (100.0 * atof(buf)); compver = *ret_XlateCompiledForOSVersion; } if (db->debugMode) fprintf(stderr,"_DtXlateGetXlateEnv: " "Platform: %s; Execution Ver: %d; Compiled Ver: %d\n", platform,execver,compver); return ret; } /*$END$*/ /*========================================================*/ /*================ Introductory Info =====================*/ /*========================================================*/ #if DOC /*========================================================*/ $INTROBEG$: _DtXlate family $1LINER$: API to translate any value to a standard value and back again $SUMMARY$: _DtXlate is a collection of routines that allow translation between platform, version, and operation specific values into standard values and back again. Translation is based on the contents of database files specified using Xrm-style resources. The semantics of the translation are a combination of standard semantics for specifying platform, version, operation, and translation type, and caller-specific semantics for the standard value format and operation values. The API contains routines to process translation databases in a useful manner. _DtXlateOpenDb() opens a particular translation database _DtXlateOpenAndMergeDb() opens a particular database and merges it with an already opened one, overriding any repeated specifications. _DtXlateOpenAllDbs() opens all occurrences of a translation database found in a search path and cumulatively merges the contents, allowing for the override of existing specifications as well as the addition of new ones. _DtXlateCloseDb() closes an open database _DtXlateOpToStdValue() translates an platform, version, and operation specific value into a standard value. _DtXlateStdToOpValue() translates a standard value into a platform, version, and operation specific value. /*=$END$================================================*/ #endif #if DOC /*========================================================*/ $INTROBEG$: Design and Implementation Considerations $1LINER$: Factors influencing design and implementation $SUMMARY$: The syntax of the translation specification table is designed to be compatible with Xrm resource specifications. This allows Xrm to be used to load, parse, and merge the databases for processing. This also causes the specifications to be case-sensitive, as case is preserved by Xrm and is used when processing queries. However, due to the semantics of translation specifications and limitations of Xrm, XrmGetResource() queries generally will not be useful. Rather, a pattern matching API is provided that implements the query capability used for the translations. The initial implementation will use XrmEnumerateDatabase(). Using Xrm leads to an in-memory approach to translation, meaning that all the resources files are parsed and loaded into memory as the first step of a translation. A line-at-a-time approach, that process one line at a time directly from the file in a grep- or awk-like manner, is likely more memory-efficient. Note that the line-at-a-time approach does not avoid parsing all the specification files, as the API supports inheritence and override of specifications. Hence, to ensure the correct value, the entire file set must be processed. Note also that in a situation where translations will be repeatedly performed, the Xrm in-memory approach may be more time-efficient, as the files need only be parsed once, and then are utilized repeatedly. Because of time constraints, a line-at-a-time approach will not be used for the first implementation, as Xrm provides all the necessary parsing, filtering, and symbol hashing routines for free. Given the likely large size of the tables and their infrequent use, a line-at-a-time approach is likely the better choice. /*=$END$================================================*/ #endif #if DOC /*========================================================*/ $INTROBEG$: translation BNF syntax and semantics $1LINER$: _DtXlate translation specification syntax and semantics $SUMMARY$: BNF Syntax of Translation Specification ======================================= ::= ( | | )* ::= ... .: (|) ::= | ::= [+ | (- )] | ::= [',' ]* | ::= '<' | '=' | '~' | '>' | '0' ::= [.]* ::= | ::= | ::= | ::= (|)+ | '"'(|)+'"' ::= '?'[] ::= '\n' ::= '!' [^]* ::= [0-9]+ ::= [-_a-ZA-Z0-9] ::= any non-whitespace character; meta and regular expression chars must be escaped with "\\" ::= any printable character; meta and regular expression chars must be escaped with "\\" ::= '!' | '"' | | | ::= ::= see regexp(5) ::= the backslash character (\). Semantics of the Translation Specification ========================================== : a file containing zero or more translation specifications or comments : a translation specification defines a fully qualified string value which may be recovered by query pattern matching the specification. The qualifiers identify the semantics of the usage of the value, allowing queries to be meaningful. The entire xlatespec must be on one line of the file. : a CDE-standardized identifier for an operating system platform. The platform need not be supported by CDE, but CDE must have standardized an identifier for the platform in order for it to be used. For example, candidate platform identifiers are "HP-UX", "AIX", "SunOS", "Solaris", "SCO", etc. These identifiers are the values returned by uname(2):utsname.sysname and uname(1) -s, and I propose CDE standardize on using these values. The identifier string and matching constant value must be added to the source code of the translation routine in order for it to be recognized. This allows control over the platform registry and enables automatic generation of the platform on the execution host through the use of #ifdef in the source code. : a platform-specific numeric value or range of numberic values that allows the value to be qualified to a particular version and release (version + release = version) or range of versions and releases of the platform. The version numbers must be integral and suitable for numeric comparison. The '+' can be used to specify an open upwards bound, as in 900+. The '- ' can be used to specify an upper bound, as in 900-999. If no range is specified, the version number must match exactly to the query pattern and/or execution environment. Platforms may not directly provide version numbers in a numeric format suitable for use when comparing with a translation specification. As part of the translation API source code, conversion routines must be supplied that translate the platform- specific values, such as those provided by uname(2), into a translation specification-compliant format suitable for comparison to . Understanding the conversion routines operation will be necessary to ensure correct translation specifications. : a CDE-standardized identifier for the operation(s) to which the value applies. The operation(s) need not be supported by every platform, but CDE must have standardized an identifier for the operation in order for it to be used. More than one identifier may be included by concatenating them using the ',' separator, eg. "iconv1,iconv3". : The field records the direction of translation that the specification supports: '<', '=', '~', '>', '0'. Use '<' if the contains a regular expression; the specification may only be used to match an operation value in a _DtLcxXlateOpToStd() translation. Use '>' if the contains value replacement expressions using values from the strings; the specification may only be used by a _DtLcxXlateStdToOp() translation. Use '=' if the is a pure string that may be used for either OpToStd() or StdToOp() translations, and the specification op-value must exactly match the op-value of the translation request. use '~' if the is a pure string that may be used for either OpToStd() or StdToOp() translations, and the specifation op-value must be a string within the op-value of the translation request. Use the '0' translation to represent that this particular translation should result in an error, that is should not result in a translation. Translations are two-way operations: from a host- and operation-specific value to standardized value, and back again. In many cases, a straight string value for string value translation is sufficient. But often, more sophisticated pattern matching and value generation mechanisms are useful to keep the table size small and as general as possible. In these cases, wildcard and regular expression operators are useful. Unfortunately, these specifications are not bi-direction. That is, the pattern matching string can be used to match against a pattern, but not to generate a result string. For example, the vi expression s/he*lo/hello/ matches "hello" as well as "hey look out below", but the reverse direction of "hello"->"he*lo" will not work. : a sequence of one or more '.'-separated CDE-standardizd identifiers or matchall characters. This represents the canonical string used as a standard representation of a semantic value that may vary in different situations. : a string that has different uses depending on the value of . If transtype is '<', the string is used as a regular expression to match the opValue in _DtLcxXlateOpToStd() requests. If the transtype is '>', the string is used as a replacement expression to generate a opValue in _DtLcxXlateStdToOp(). If transtype is '=' or '~', the string is used as a straight string to both match for the OpToStd translation and as a value for the StdToOp translation. % If the opvalue contains whitespace, the opvalue must be enclosed in double quote characters. If the opvalue contains any meta characters that should not be treated as meta characters, they must be escaped by two preceding backslashs.Lack of an opvalue is not an error, but will be ignored during matches and return an empty string in a StdToOp translation; to specify that a given translation does not exist, the '0' translation type should be used or the specification should not occur. : a comment begins on an empty line with an unescaped ! and continues to the end of the line. : The matchall character is a universal quantifier, meaning that it symbolizes all possible values of the field where it is used. The matchall character may occur only in the qualifers side of a translation specification. Traditionally, the matchall character has been '*', but because of the semantics of Xrm, if the '*' is used as the matchall, Xrm does not restrict enumeration as needed. The matchall character may be followed by a number when the is '>'. When this occurs, the string that matches the matchall character may be referenced in the by using the sequence \, as in "\1". The occurrence is replaced with the matched string. /*=$END$================================================*/ #endif #if DOC /*========================================================*/ $INTROBEG$: translation specification examples $1LINER$: examples of _DtXlate translation specifications $EXAMPLE$: !! These are examples of bi-directional straight translations HP-UX.900-999.setlocale.=.en_US.hp-roman8: american HP-UX.900-999.setlocale.=.en_US.iso88591: american.iso88591 HP-UX.900-999.setlocale.=.nl_BE.hp-roman8.fold: dutch@fold HP-UX.900-999.setlocale.=.nl_BE.hp-roman8.nofold: dutch@nofold !! These are examples of OpToStd translations utilizing regular !! expression patterns to match the search value. HP-UX.?.version.<.900: "A\\.09\\..*" !! any HPUX 9.x version HP-UX.?.version.<.807: "A\\.08\\.07.*" HP-UX.?.version.<.800: "A\\.08\\..*" AIX.?.version.<.320: "2 3" AIX.?.version.<.300: "[0-9] 3" SunOS.?.version.<.530: "5\\.3.*" SunOS.?.version.<.500: "5\\..*" !! These are examples of StdToOp translations utilizing matchall !! specifiers in the std value to match the search value. HP-UX.900+.iconv1.>.?.iso88596: arabic8 HP-UX.900+.iconv1.>.?.iso88597: greek8 HP-UX.900+.iconv1.>.?.hp-kana8: kana8 HP-UX.900+.iconv1.>.?.hp-roman8: roman8 !! The following examples use value replacement as part of their !! specifications. Using this can lead to much sparser tables, but !! it depends on op-specific and std values sharing the same strings. HP-UX.900-999.setlocale.<.nl_BE.hp-roman8.?1: [dD][uU][tT][cC][hH]@\\(.*\\) !HP-UX.900-999.setlocale.<.nl_BE.hp-roman8.?1: dutch@\\(.*\\) HP-UX.1000+.setlocale.>.?1.hp-roman8: \\1.roman8 !! all non-modif cases HP-UX.1000+.setlocale.>.?1.hp-roman8.?2: \\1.roman8@\\2 !! all modif cases /*=$END$================================================*/ #endif #if DOC /*========================================================*/ $INTROBEG$: _DtXlate example usage $1LINER$: Examples of how to _DtXlate $EXAMPLE$: #include main() { _DtXlateDb db = NULL; int ret; char plat[_DtPLATFORM_MAX_LEN]; int execver; int compver; char * val = NULL; char * str = NULL; char empty = 0; #define OPER_YOUROP "myop" env = getenv("MYPATH"); if (env == NULL) env = ∅ ret = _DtXlateOpenAllDbs(env,"myfile.xlt",&db); ret = _DtXlateGetXlateEnv(db,plat,&execver,&compver); printf("Platform: %s\nExec Ver: %d\nComp Ver: %d\n", plat,execver,compver); ret = _DtXlateStdToOpValue(db,plat,compver,OPER_YOUROP, str="en_US.hp-roman8",&val,NULL); if (ret==0) printf("setlocale(%s) xlation=%s\n", str, val); else printf("no xlation\n", val); ret = _DtXlateOpToStdValue(db,plat,execver,OPER_YOUROP, str="american",&val,NULL); if (ret==0) printf("setlocale(%s) xlation=%s\n", str, val); else printf("no xlation\n", val); ret = _DtXlateCloseDb(&db); } /*=$END$================================================*/ #endif