| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282 | /* *  $Id: configfile.c,v 4.7 2005/04/16 16:44:28 bkorb Exp $ *  Time-stamp:      "2005-04-03 15:53:54 bkorb" * *  configuration/rc/ini file handling. *//* *  Automated Options copyright 1992-2005 Bruce Korb * *  Automated Options is free software. *  You may redistribute it and/or modify it under the terms of the *  GNU General Public License, as published by the Free Software *  Foundation; either version 2, or (at your option) any later version. * *  Automated Options is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with Automated Options.  See the file "COPYING".  If not, *  write to:  The Free Software Foundation, Inc., *             59 Temple Place - Suite 330, *             Boston,  MA  02111-1307, USA. * * As a special exception, Bruce Korb gives permission for additional * uses of the text contained in his release of AutoOpts. * * The exception is that, if you link the AutoOpts library with other * files to produce an executable, this does not by itself cause the * resulting executable to be covered by the GNU General Public License. * Your use of that executable is in no way restricted on account of * linking the AutoOpts library code into it. * * This exception does not however invalidate any other reasons why * the executable file might be covered by the GNU General Public License. * * This exception applies only to the code released by Bruce Korb under * the name AutoOpts.  If you copy code from other sources under the * General Public License into a copy of AutoOpts, as the General Public * License permits, the exception does not apply to the code that you add * in this way.  To avoid misleading anyone as to the status of such * modified files, you must delete this exception notice from them. * * If you write modifications of your own for AutoOpts, it is your choice * whether to permit this exception to apply to your modifications. * If you do not wish that, delete this exception notice. *//* = = = START-STATIC-FORWARD = = = *//* static forward declarations maintained by :mkfwd */static voidfilePreset(    tOptions*     pOpts,    const char*   pzFileName,    int           direction );static char*handleComment( char* pzText );static char*handleConfig(    tOptions*     pOpts,    tOptState*    pOS,    char*         pzText,    int           direction );static char*handleDirective(    tOptions*     pOpts,    char*         pzText );static char*handleProgramSection(    tOptions*     pOpts,    char*         pzText );static char*handleStructure(    tOptions*     pOpts,    tOptState*    pOS,    char*         pzText,    int           direction );static char*parseKeyWordType(    tOptions*     pOpts,    char*         pzText,    tOptionValue* pType );static char*parseLoadMode(    char*               pzText,    tOptionLoadMode*    pMode );static char*parseSetMemType(    tOptions*     pOpts,    char*         pzText,    tOptionValue* pType );static char*parseValueType(    char*         pzText,    tOptionValue* pType );static char*skipUnknown( char* pzText );/* = = = END-STATIC-FORWARD = = = *//*=export_func  configFileLoad * * what:  parse a configuration file * arg:   + const char*     + pzFile + the file to load + * * ret_type:  const tOptionValue* * ret_desc:  An allocated, compound value structure * * doc: *  This routine will load a named configuration file and parse the *  text as a hierarchically valued option.  The option descriptor *  created from an option definition file is not used via this interface. *  The returned value is "named" with the input file name and is of *  type "@code{OPARG_TYPE_HIERARCHY}".  It may be used in calls to *  @code{optionGetValue()}, @code{optionNextValue()} and *  @code{optionUnloadNested()}. * * err: *  If the file cannot be loaded or processed, @code{NULL} is returned and *  @var{errno} is set.  It may be set by a call to either @code{open(2)} *  @code{mmap(2)} or other file system calls, or it may be: *  @itemize @bullet *  @item *  @code{ENOENT} - the file was empty. *  @item *  @code{EINVAL} - the file contents are invalid -- not properly formed. *  @item *  @code{ENOMEM} - not enough memory to allocate the needed structures. *  @end itemize=*/const tOptionValue*configFileLoad( const char* pzFile ){    tmap_info_t   cfgfile;    tOptionValue* pRes = NULL;    char* pzText =        text_mmap( pzFile, PROT_READ, MAP_PRIVATE, &cfgfile );    if (pzText == MAP_FAILED)        return NULL; /* errno is set */    pRes = optionLoadNested(pzText, pzFile, strlen(pzFile), OPTION_LOAD_COOKED); all_done:    if (pRes == NULL) {        int err = errno;        text_munmap( &cfgfile );        errno = err;    } else        text_munmap( &cfgfile );    return pRes;}/*=export_func  optionFindValue * * what:  find a hierarcicaly valued option instance * arg:   + const tOptDesc* + pOptDesc + an option with a nested arg type + * arg:   + const char*     + name     + name of value to find + * arg:   + const char*     + value    + the matching value    + * * ret_type:  const tOptionValue* * ret_desc:  a compound value structure * * doc: *  This routine will find an entry in a nested value option or configurable. *  It will search through the list and return a matching entry. * * err: *  The returned result is NULL and errno is set: *  @itemize @bullet *  @item *  @code{EINVAL} - the @code{pOptValue} does not point to a valid *  hierarchical option value. *  @item *  @code{ENOENT} - no entry matched the given name. *  @end itemize=*/const tOptionValue*optionFindValue( const tOptDesc* pOptDesc,                 const char* pzName, const char* pzVal ){    const tOptionValue* pRes = NULL;    if (  (pOptDesc == NULL)       || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY))  {        errno = EINVAL;    }    else if (pOptDesc->optCookie == NULL) {        errno = ENOENT;    }    else do {        tArgList* pAL = pOptDesc->optCookie;        int ct = pAL->useCt;        const tOptionValue** ppOV =            (const tOptionValue**)(void*)&(pAL->apzArgs);        if (ct == 0) {            errno = ENOENT;            break;        }        if (pzName == NULL) {            pRes = *ppOV;            break;        }        while (--ct >= 0) {            const tOptionValue* pOV = *(ppOV++);            const tOptionValue* pRV = optionGetValue( pOV, pzName );            if (pRV == NULL)                continue;            if (pzVal == NULL) {                pRes = pOV;                break;            }        }        if (pRes == NULL)            errno = ENOENT;    } while (0);    return pRes;}/*=export_func  optionFindNextValue * * what:  find a hierarcicaly valued option instance * arg:   + const tOptDesc* + pOptDesc + an option with a nested arg type + * arg:   + const tOptionValue* + pPrevVal + the last entry + * arg:   + const char*     + name     + name of value to find + * arg:   + const char*     + value    + the matching value    + * * ret_type:  const tOptionValue* * ret_desc:  a compound value structure * * doc: *  This routine will find the next entry in a nested value option or *  configurable.  It will search through the list and return the next entry *  that matches the criteria. * * err: *  The returned result is NULL and errno is set: *  @itemize @bullet *  @item *  @code{EINVAL} - the @code{pOptValue} does not point to a valid *  hierarchical option value. *  @item *  @code{ENOENT} - no entry matched the given name. *  @end itemize=*/const tOptionValue*optionFindNextValue( const tOptDesc* pOptDesc, const tOptionValue* pPrevVal,                 const char* pzName, const char* pzVal ){    int foundOldVal = 0;    tOptionValue* pRes = NULL;    if (  (pOptDesc == NULL)       || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY))  {        errno = EINVAL;    }    else if (pOptDesc->optCookie == NULL) {        errno = ENOENT;    }    else do {        tArgList* pAL = pOptDesc->optCookie;        int ct = pAL->useCt;        tOptionValue** ppOV = (tOptionValue**)(void*)&(pAL->apzArgs);        if (ct == 0) {            errno = ENOENT;            break;        }        while (--ct >= 0) {            tOptionValue* pOV = *(ppOV++);            if (foundOldVal) {                pRes = pOV;                break;            }            if (pOV == pPrevVal)                foundOldVal = 1;        }        if (pRes == NULL)            errno = ENOENT;    } while (0);    return pRes;}/*=export_func  optionGetValue * * what:  get a specific value from a hierarcical list * arg:   + const tOptionValue* + pOptValue + a hierarchcal value + * arg:   + const char*   + valueName + name of value to get + * * ret_type:  const tOptionValue* * ret_desc:  a compound value structure * * doc: *  This routine will find an entry in a nested value option or configurable. *  If "valueName" is NULL, then the first entry is returned.  Otherwise, *  the first entry with a name that exactly matches the argument will be *  returned. * * err: *  The returned result is NULL and errno is set: *  @itemize @bullet *  @item *  @code{EINVAL} - the @code{pOptValue} does not point to a valid *  hierarchical option value. *  @item *  @code{ENOENT} - no entry matched the given name. *  @end itemize=*/const tOptionValue*optionGetValue( const tOptionValue* pOld, const char* pzValName ){    tArgList*     pAL;    tOptionValue* pRes = NULL;    if ((pOld == NULL) || (pOld->valType != OPARG_TYPE_HIERARCHY)) {        errno = EINVAL;        return NULL;    }    pAL = pOld->v.nestVal;    if (pAL->useCt > 0) {        int ct = pAL->useCt;        tOptionValue** papOV = (tOptionValue**)(pAL->apzArgs);        if (pzValName == NULL) {            pRes = *papOV;        }        else do {            tOptionValue* pOV = *(papOV++);            if (strcmp( pOV->pzName, pzValName ) == 0) {                pRes = pOV;                break;            }        } while (--ct > 0);    }    if (pRes == NULL)        errno = ENOENT;    return pRes;}/*=export_func  optionNextValue * * what:  get the next value from a hierarchical list * arg:   + const tOptionValue* + pOptValue + a hierarchcal list value + * arg:   + const tOptionValue* + pOldValue + a value from this list   + * * ret_type:  const tOptionValue* * ret_desc:  a compound value structure * * doc: *  This routine will return the next entry after the entry passed in.  At the *  end of the list, NULL will be returned.  If the entry is not found on the *  list, NULL will be returned and "@var{errno}" will be set to EINVAL. *  The "@var{pOldValue}" must have been gotten from a prior call to this *  routine or to "@code{opitonGetValue()}". * * err: *  The returned result is NULL and errno is set: *  @itemize @bullet *  @item *  @code{EINVAL} - the @code{pOptValue} does not point to a valid *  hierarchical option value or @code{pOldValue} does not point to a *  member of that option value. *  @item *  @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry. *  @end itemize=*/const tOptionValue*optionNextValue( const tOptionValue* pOVList, const tOptionValue* pOldOV ){    tArgList*     pAL;    tOptionValue* pRes = NULL;    int           err  = EINVAL;    if ((pOVList == NULL) || (pOVList->valType != OPARG_TYPE_HIERARCHY)) {        errno = EINVAL;        return NULL;    }    pAL = pOVList->v.nestVal;    {        int   ct   = pAL->useCt;        tOptionValue** papNV = (tOptionValue**)(pAL->apzArgs);        while (ct-- > 0) {            tOptionValue* pNV = *(papNV++);            if (pNV == pOldOV) {                if (ct == 0) {                    err = ENOENT;                } else {                    err  = 0;                    pRes = *papNV;                }                break;            }        }    }    if (err != 0)        errno = err;    return pRes;}/*  filePreset * *  Load a file containing presetting information (a configuration file). */static voidfilePreset(    tOptions*     pOpts,    const char*   pzFileName,    int           direction ){    tmap_info_t   cfgfile;    char*         pzFileText =        text_mmap( pzFileName, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile );    char*         pzEndText;    tOptState     st = OPTSTATE_INITIALIZER(PRESET);    if (pzFileText == MAP_FAILED)        return;    if (direction == DIRECTION_CALLED) {        st.flags  = OPTST_DEFINED;        direction = DIRECTION_PROCESS;    }    /*     *  IF this is called via "optionProcess", then we are presetting.     *  This is the default and the PRESETTING bit will be set.     *  If this is called via "optionFileLoad", then the bit is not set     *  and we consider stuff set herein to be "set" by the client program.     */    if ((pOpts->fOptSet & OPTPROC_PRESETTING) == 0)        st.flags = OPTST_SET;    pzEndText = pzFileText + cfgfile.txt_size;    do  {        while (isspace( *pzFileText ))  pzFileText++;        if (isalpha( *pzFileText )) {            pzFileText = handleConfig( pOpts, &st, pzFileText, direction );        } else switch (*pzFileText) {        case '<':            if (isalpha( pzFileText[1] ))                pzFileText = handleStructure(pOpts, &st, pzFileText, direction);            else switch (pzFileText[1]) {            case '?':                pzFileText = handleDirective( pOpts, pzFileText );                break;            case '!':                pzFileText = handleComment( pzFileText );                break;            case '/':                pzFileText = strchr( pzFileText+2, '>' );                if (pzFileText++ != NULL)                    break;            default:                goto all_done;            }            break;        case '[':            pzFileText = handleProgramSection( pOpts, pzFileText );            break;        case '#':            pzFileText = strchr( pzFileText+1, '\n' );            break;        default:            goto all_done; /* invalid format */        }    } while (pzFileText != NULL); all_done:    text_munmap( &cfgfile );}/*  handleComment * *  "pzText" points to a "<!" sequence. *  Theoretically, we should ensure that it begins with "<!--", *  but actually I don't care that much.  It ends with "-->". */static char*handleComment( char* pzText ){    char* pz = strstr( pzText, "-->" );    if (pz != NULL)        pz += 3;    return pz;}/*  handleConfig * *  "pzText" points to the start of some value name. *  The end of the entry is the end of the line that is not preceded by *  a backslash escape character.  The string value is always processed *  in "cooked" mode. */static char*handleConfig(    tOptions*     pOpts,    tOptState*    pOS,    char*         pzText,    int           direction ){    char* pzName = pzText++;    char* pzEnd  = strchr( pzText, '\n' );    while (ISNAMECHAR( *pzText ))  pzText++;    while (isspace( *pzText )) pzText++;    if (pzText > pzEnd) {    name_only:        *pzEnd++ = NUL;        loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED );        return pzEnd;    }    /*     *  Either the first character after the name is a ':' or '=',     *  or else we must have skipped over white space.  Anything else     *  is an invalid format and we give up parsing the text.     */    if ((*pzText == '=') || (*pzText == ':')) {        while (isspace( *++pzText ))   ;        if (pzText > pzEnd)            goto name_only;    } else if (! isspace(pzText[-1]))        return NULL;    /*     *  IF the value is continued, remove the backslash escape and push "pzEnd"     *  on to a newline *not* preceded by a backslash.     */    if (pzEnd[-1] == '\\') {        char* pcD = pzEnd-1;        char* pcS = pzEnd;        for (;;) {            char ch = *(pcS++);            switch (ch) {            case NUL:                pcS = NULL;            case '\n':                *pcD = NUL;                pzEnd = pcS;                goto copy_done;            case '\\':                if (*pcS == '\n') {                    ch = *(pcS++);                }                /* FALLTHROUGH */            default:                *(pcD++) = ch;            }        } copy_done:;    } else {        /*         *  The newline was not preceded by a backslash.  NUL it out         */        *(pzEnd++) = NUL;    }    /*     *  "pzName" points to what looks like text for one option/configurable.     *  It is NUL terminated.  Process it.     */    loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED );    return pzEnd;}/*  handleDirective * *  "pzText" points to a "<?" sequence. *  For the moment, we only handle "<?program" directives. */static char*handleDirective(    tOptions*     pOpts,    char*         pzText ){    char   ztitle[32] = "<?";    size_t title_len = strlen( zProg );    size_t name_len;    if (  (strncmp( pzText+2, zProg, title_len ) != 0)       || (! isspace( pzText[title_len+2] )) )  {        pzText = strchr( pzText+2, '>' );        if (pzText != NULL)            pzText++;        return pzText;    }    name_len = strlen( pOpts->pzProgName );    strcpy( ztitle+2, zProg );    title_len += 2;    do  {        pzText += title_len;        if (isspace(*pzText)) {            while (isspace(*pzText))  pzText++;            if (  (strneqvcmp( pzText, pOpts->pzProgName, name_len ) == 0)               && (pzText[name_len] == '>'))  {                pzText += name_len + 1;                break;            }        }        pzText = strstr( pzText, ztitle );    } while (pzText != NULL);    return pzText;}/*  handleProgramSection * *  "pzText" points to a '[' character. *  The "traditional" [PROG_NAME] segmentation of the config file. *  Do not ever mix with the "<?program prog-name>" variation. */static char*handleProgramSection(    tOptions*     pOpts,    char*         pzText ){    size_t len = strlen( pOpts->pzPROGNAME );    if (   (strncmp( pzText+1, pOpts->pzPROGNAME, len ) == 0)        && (pzText[len+1] == ']'))        return strchr( pzText + len + 2, '\n' );    if (len > 16)        return NULL;    {        char z[24];        sprintf( z, "[%s]", pOpts->pzPROGNAME );        pzText = strstr( pzText, z );    }    if (pzText != NULL)        pzText = strchr( pzText, '\n' );    return pzText;}/*  handleStructure * *  "pzText" points to a '<' character, followed by an alpha. *  The end of the entry is either the "/>" following the name, or else a *  "</name>" string. */static char*handleStructure(    tOptions*     pOpts,    tOptState*    pOS,    char*         pzText,    int           direction ){    tOptionLoadMode mode = OPTION_LOAD_UNCOOKED;    tOptionValue     valu;    char* pzName = ++pzText;    char* pcNulPoint;    char* pzValStart;    while (ISNAMECHAR( *pzText ))  pzText++;    pcNulPoint = pzText;    valu.valType = OPARG_TYPE_STRING;    switch (*pzText) {    case ' ':    case '\t':        pzText = parseAttributes( pOpts, pzText, &mode, &valu );        if (*pzText == '>')            break;        if (*pzText != '/')            return NULL;    case '/':        if (pzText[1] != '>')            return NULL;        *pzText = NUL;        pzText += 2;        loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_KEEP );        return pzText;    case '>':        break;    default:        pzText = strchr( pzText, '>');        if (pzText != NULL)            pzText++;        return pzText;    }    pzValStart = ++pzText;    /*     *  If we are here, we have a value.  Separate the name from the     *  value for a moment.     */    *pcNulPoint = NUL;    /*     *  Find the end of the option text and NUL terminate it     */    {        char   z[64], *pz = z;        size_t len = strlen(pzName) + 4;        if (len > sizeof(z))            pz = AGALOC(len, "scan name");        sprintf( pz, "</%s>", pzName );        *pzText = ' ';        pzText = strstr( pzText, pz );        if (pz != z) free(pz);        if (pzText == NULL)            return pzText;        *pzText = NUL;        pzText += len-1;    }    /*     *  Rejoin the name and value for parsing by "loadOptionLine()".     */    *(pcNulPoint++) = ' ';    /*     *  "pzName" points to what looks like text for one option/configurable.     *  It is NUL terminated.  Process it.     */    loadOptionLine( pOpts, pOS, pzName, direction, mode );    return pzText;}/*  internalFileLoad * *  Load a configuration file.  This may be invoked either from *  scanning the "homerc" list, or from a specific file request. *  (see "optionFileLoad()", the implementation for --load-opts) */LOCAL voidinternalFileLoad( tOptions* pOpts ){    int     idx;    int     inc = DIRECTION_PRESET;    char    zFileName[ 4096 ];    if (pOpts->papzHomeList == NULL)        return;    /*     *  Find the last RC entry (highest priority entry)     */    for (idx = 0; pOpts->papzHomeList[ idx+1 ] != NULL; ++idx)  ;    /*     *  For every path in the home list, ...  *TWICE* We start at the last     *  (highest priority) entry, work our way down to the lowest priority,     *  handling the immediate options.     *  Then we go back up, doing the normal options.     */    for (;;) {        struct stat StatBuf;        cch_t*  pzPath;        /*         *  IF we've reached the bottom end, change direction         */        if (idx < 0) {            inc = DIRECTION_PROCESS;            idx = 0;        }        pzPath = pOpts->papzHomeList[ idx ];        /*         *  IF we've reached the top end, bail out         */        if (pzPath == NULL)            break;        idx += inc;        if (! optionMakePath( zFileName, sizeof( zFileName ),                              pzPath, pOpts->pzProgPath ))            continue;        /*         *  IF the file name we constructed is a directory,         *  THEN append the Resource Configuration file name         *  ELSE we must have the complete file name         */        if (stat( zFileName, &StatBuf ) != 0)            continue; /* bogus name - skip the home list entry */        if (S_ISDIR( StatBuf.st_mode )) {            size_t len = strlen( zFileName );            char* pz;            if (len + 1 + strlen( pOpts->pzRcName ) >= sizeof( zFileName ))                continue;            pz = zFileName + len;            if (pz[-1] != '/')                *(pz++) = '/';            strcpy( pz, pOpts->pzRcName );        }        filePreset( pOpts, zFileName, inc );        /*         *  IF we are now to skip config files AND we are presetting,         *  THEN change direction.  We must go the other way.         */        if (SKIP_RC_FILES(pOpts) && PRESETTING(inc)) {            idx -= inc;  /* go back and reprocess current file */            inc =  DIRECTION_PROCESS;        }    } /* For every path in the home list, ... */}/*=export_func optionFileLoad * * what: Load the locatable config files, in order * * arg:  + tOptions*   + pOpts  + program options descriptor + * arg:  + const char* + pzProg + program name + * * ret_type:  int * ret_desc:  0 -> SUCCESS, -1 -> FAILURE * * doc: * * This function looks in all the specified directories for a configuration * file ("rc" file or "ini" file) and processes any found twice.  The first * time through, they are processed in reverse order (last file first).  At * that time, only "immediate action" configurables are processed.  For * example, if the last named file specifies not processing any more * configuration files, then no more configuration files will be processed. * Such an option in the @strong{first} named directory will have no effect. * * Once the immediate action configurables have been handled, then the * directories are handled in normal, forward order.  In that way, later * config files can override the settings of earlier config files. * * See the AutoOpts documentation for a thorough discussion of the * config file format. * * Configuration files not found or not decipherable are simply ignored. * * err:  Returns the value, "-1" if the program options descriptor *       is out of date or indecipherable.  Otherwise, the value "0" will *       always be returned.=*/intoptionFileLoad( tOptions* pOpts, const char* pzProgram ){    if (! SUCCESSFUL( validateOptionsStruct( pOpts, pzProgram )))        return -1;    pOpts->pzProgName = pzProgram;    internalFileLoad( pOpts );    return 0;}/*=export_func  optionLoadOpt * private: * * what:  Load an option rc/ini file * arg:   + tOptions* + pOpts    + program options descriptor + * arg:   + tOptDesc* + pOptDesc + the descriptor for this arg + * * doc: *  Processes the options found in the file named with pOptDesc->pzLastArg.=*/voidoptionLoadOpt( tOptions* pOpts, tOptDesc* pOptDesc ){    /*     *  IF the option is not being disabled,     *  THEN load the file.  There must be a file.     *  (If it is being disabled, then the disablement processing     *  already took place.  It must be done to suppress preloading     *  of ini/rc files.)     */    if (! DISABLED_OPT( pOptDesc )) {        struct stat sb;        if (stat( pOptDesc->pzLastArg, &sb ) != 0) {            if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)                return;            fprintf( stderr, zFSErrOptLoad, errno, strerror( errno ),                     pOptDesc->pzLastArg );            (*pOpts->pUsageProc)( pOpts, EXIT_FAILURE );            /* NOT REACHED */        }        if (! S_ISREG( sb.st_mode )) {            if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)                return;            fprintf( stderr, zNotFile, pOptDesc->pzLastArg );            (*pOpts->pUsageProc)( pOpts, EXIT_FAILURE );            /* NOT REACHED */        }        filePreset(pOpts, pOptDesc->pzLastArg, DIRECTION_CALLED);    }}/*  parseAttributes * *  Parse the various attributes of an XML-styled config file entry */LOCAL char*parseAttributes(    tOptions*           pOpts,    char*               pzText,    tOptionLoadMode*    pMode,    tOptionValue*       pType ){    size_t lenLoadType = strlen( zLoadType );    size_t lenKeyWords = strlen( zKeyWords );    size_t lenSetMem   = strlen( zSetMembers );    do  {        switch (*pzText) {        case '/': pType->valType = OPARG_TYPE_NONE;        case '>': return pzText;        default:        case NUL: return NULL;        case ' ':        case '\t':        case '\n':        case '\f':        case '\r':        case '\v':            break;        }        while (isspace( *++pzText ))   ;        if (strncmp( pzText, zLoadType, lenLoadType ) == 0) {            pzText = parseValueType( pzText+lenLoadType, pType );            continue;        }        if (strncmp( pzText, zKeyWords, lenKeyWords ) == 0) {            pzText = parseKeyWordType( pOpts, pzText+lenKeyWords, pType );            continue;        }        if (strncmp( pzText, zSetMembers, lenSetMem ) == 0) {            pzText = parseSetMemType( pOpts, pzText+lenSetMem, pType );            continue;        }        pzText = parseLoadMode( pzText, pMode );    } while (pzText != NULL);    return pzText;}/*  parseKeyWordType * *  "pzText" points to the character after "words=". *  What should follow is a name of a keyword (enumeration) list. */static char*parseKeyWordType(    tOptions*     pOpts,    char*         pzText,    tOptionValue* pType ){    return skipUnknown( pzText );}/*  parseLoadMode * *  "pzText" points to some name character.  We check for "cooked" or *  "uncooked" or "keep".  This function should handle any attribute *  that does not have an associated value. */static char*parseLoadMode(    char*               pzText,    tOptionLoadMode*    pMode ){    {        size_t len = strlen(zLoadCooked);        if (strncmp( pzText, zLoadCooked, len ) == 0) {            if (  (pzText[len] == '>')               || (pzText[len] == '/')               || isspace(pzText[len])) {                *pMode = OPTION_LOAD_COOKED;                return pzText + len;            }            goto unknown;        }    }    {        size_t len = strlen(zLoadUncooked);        if (strncmp( pzText, zLoadUncooked, len ) == 0) {            if (  (pzText[len] == '>')               || (pzText[len] == '/')               || isspace(pzText[len])) {                *pMode = OPTION_LOAD_UNCOOKED;                return pzText + len;            }            goto unknown;        }    }    {        size_t len = strlen(zLoadKeep);        if (strncmp( pzText, zLoadKeep, len ) == 0) {            if (  (pzText[len] == '>')               || (pzText[len] == '/')               || isspace(pzText[len])) {                *pMode = OPTION_LOAD_KEEP;                return pzText + len;            }            goto unknown;        }    }  unknown:    return skipUnknown( pzText );}/*  parseSetMemType * *  "pzText" points to the character after "members=" *  What should follow is a name of a "set membership". *  A collection of bit flags. */static char*parseSetMemType(    tOptions*     pOpts,    char*         pzText,    tOptionValue* pType ){    return skipUnknown( pzText );}/*  parseValueType * *  "pzText" points to the character after "type=" */static char*parseValueType(    char*         pzText,    tOptionValue* pType ){    {        size_t len = strlen(zLtypeString);        if (strncmp( pzText, zLtypeString, len ) == 0) {            if ((pzText[len] == '>') || isspace(pzText[len])) {                pType->valType = OPARG_TYPE_STRING;                return pzText + len;            }            goto unknown;        }    }    {        size_t len = strlen(zLtypeInteger);        if (strncmp( pzText, zLtypeInteger, len ) == 0) {            if ((pzText[len] == '>') || isspace(pzText[len])) {                pType->valType = OPARG_TYPE_NUMERIC;                return pzText + len;            }            goto unknown;        }    }    {        size_t len = strlen(zLtypeBool);        if (strncmp( pzText, zLtypeBool, len ) == 0) {            if ((pzText[len] == '>') || isspace(pzText[len])) {                pType->valType = OPARG_TYPE_BOOLEAN;                return pzText + len;            }            goto unknown;        }    }    {        size_t len = strlen(zLtypeKeyword);        if (strncmp( pzText, zLtypeKeyword, len ) == 0) {            if ((pzText[len] == '>') || isspace(pzText[len])) {                pType->valType = OPARG_TYPE_ENUMERATION;                return pzText + len;            }            goto unknown;        }    }    {        size_t len = strlen(zLtypeSetMembership);        if (strncmp( pzText, zLtypeSetMembership, len ) == 0) {            if ((pzText[len] == '>') || isspace(pzText[len])) {                pType->valType = OPARG_TYPE_MEMBERSHIP;                return pzText + len;            }            goto unknown;        }    }    {        size_t len = strlen(zLtypeNest);        if (strncmp( pzText, zLtypeNest, len ) == 0) {            if ((pzText[len] == '>') || isspace(pzText[len])) {                pType->valType = OPARG_TYPE_HIERARCHY;                return pzText + len;            }            goto unknown;        }    }  unknown:    pType->valType = OPARG_TYPE_NONE;    return skipUnknown( pzText );}/*  skipUnknown * *  Skip over some unknown attribute */static char*skipUnknown( char* pzText ){    for (;; pzText++) {        if (isspace( *pzText ))  return pzText;        switch (*pzText) {        case NUL: return NULL;        case '/':        case '>': return pzText;        }    }}/*  validateOptionsStruct * *  Make sure the option descriptor is there and that we understand it. *  This should be called from any user entry point where one needs to *  worry about validity.  (Some entry points are free to assume that *  the call is not the first to the library and, thus, that this has *  already been called.) */LOCAL tSuccessvalidateOptionsStruct( tOptions* pOpts, const char* pzProgram ){    if (pOpts == NULL) {        fputs( zAO_Bad, stderr );        exit( EXIT_FAILURE );    }    /*     *  IF the client has enabled translation and the translation procedure     *  is available, then go do it.     */    if (  ((pOpts->fOptSet & OPTPROC_TRANSLATE) != 0)       && (pOpts->pTransProc != 0) ) {        (*pOpts->pTransProc)();        pOpts->fOptSet &= ~OPTPROC_TRANSLATE;    }    /*     *  IF the struct version is not the current, and also     *     either too large (?!) or too small,     *  THEN emit error message and fail-exit     */    if (  ( pOpts->structVersion  != OPTIONS_STRUCT_VERSION  )       && (  (pOpts->structVersion > OPTIONS_STRUCT_VERSION  )          || (pOpts->structVersion < OPTIONS_MINIMUM_VERSION )       )  )  {        fprintf( stderr, zAO_Err, pOpts->origArgVect[0],                 NUM_TO_VER( pOpts->structVersion ));        if (pOpts->structVersion > OPTIONS_STRUCT_VERSION )            fputs( zAO_Big, stderr );        else            fputs( zAO_Sml, stderr );        return FAILURE;    }    /*     *  If the program name hasn't been set, then set the name and the path     *  and the set of equivalent characters.     */    if (pOpts->pzProgName == NULL) {        const char* pz = strrchr( pzProgram, '/' );        if (pz == NULL)             pOpts->pzProgName = pzProgram;        else pOpts->pzProgName = pz+1;        pOpts->pzProgPath = pzProgram;        /*         *  when comparing long names, these are equivalent         */        strequate( zSepChars );    }    return SUCCESS;}/** * Local Variables: * mode: C * c-file-style: "stroustrup" * tab-width: 4 * indent-tabs-mode: nil * End: * end of autoopts/configfile.c */
 |