File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / sntp / libopts / save.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:38 2012 UTC (12 years, 7 months ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

    1: 
    2: /*
    3:  * \file save.c
    4:  *
    5:  * Time-stamp:      "2011-04-06 09:21:44 bkorb"
    6:  *
    7:  *  This module's routines will take the currently set options and
    8:  *  store them into an ".rc" file for re-interpretation the next
    9:  *  time the invoking program is run.
   10:  *
   11:  *  This file is part of AutoOpts, a companion to AutoGen.
   12:  *  AutoOpts is free software.
   13:  *  AutoOpts is Copyright (c) 1992-2011 by Bruce Korb - all rights reserved
   14:  *
   15:  *  AutoOpts is available under any one of two licenses.  The license
   16:  *  in use must be one of these two and the choice is under the control
   17:  *  of the user of the license.
   18:  *
   19:  *   The GNU Lesser General Public License, version 3 or later
   20:  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
   21:  *
   22:  *   The Modified Berkeley Software Distribution License
   23:  *      See the file "COPYING.mbsd"
   24:  *
   25:  *  These files have the following md5sums:
   26:  *
   27:  *  43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
   28:  *  06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
   29:  *  66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
   30:  */
   31: 
   32: static char const  zWarn[] = "%s WARNING:  cannot save options - ";
   33: static char const close_xml[] = "</%s>\n";
   34: 
   35: /* = = = START-STATIC-FORWARD = = = */
   36: static tCC*
   37: findDirName(tOptions* pOpts, int* p_free);
   38: 
   39: static char const *
   40: findFileName(tOptions * pOpts, int * p_free_name);
   41: 
   42: static void
   43: printEntry(
   44:     FILE *     fp,
   45:     tOptDesc * p,
   46:     tCC*       pzLA );
   47: 
   48: static void
   49: print_a_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp);
   50: 
   51: static void
   52: print_a_string(FILE * fp, char const * name, char const * pz);
   53: 
   54: static void
   55: printValueList(FILE * fp, char const * name, tArgList * al);
   56: 
   57: static void
   58: printHierarchy(FILE * fp, tOptDesc * p);
   59: 
   60: static FILE *
   61: openSaveFile(tOptions* pOpts);
   62: 
   63: static void
   64: printNoArgOpt(FILE * fp, tOptDesc * p, tOptDesc * pOD);
   65: 
   66: static void
   67: printStringArg(FILE * fp, tOptDesc * pOD);
   68: 
   69: static void
   70: printEnumArg(FILE * fp, tOptDesc * pOD);
   71: 
   72: static void
   73: printSetMemberArg(FILE * fp, tOptDesc * pOD);
   74: 
   75: static void
   76: printFileArg(FILE * fp, tOptDesc * pOD, tOptions* pOpts);
   77: /* = = = END-STATIC-FORWARD = = = */
   78: 
   79: static tCC*
   80: findDirName(tOptions* pOpts, int* p_free)
   81: {
   82:     tCC*  pzDir;
   83: 
   84:     if (  (pOpts->specOptIdx.save_opts == NO_EQUIVALENT)
   85:        || (pOpts->specOptIdx.save_opts == 0))
   86:         return NULL;
   87: 
   88:     pzDir = pOpts->pOptDesc[ pOpts->specOptIdx.save_opts ].optArg.argString;
   89:     if ((pzDir != NULL) && (*pzDir != NUL))
   90:         return pzDir;
   91: 
   92:     /*
   93:      *  This function only works if there is a directory where
   94:      *  we can stash the RC (INI) file.
   95:      */
   96:     {
   97:         tCC* const* papz = pOpts->papzHomeList;
   98:         if (papz == NULL)
   99:             return NULL;
  100: 
  101:         while (papz[1] != NULL) papz++;
  102:         pzDir = *papz;
  103:     }
  104: 
  105:     /*
  106:      *  IF it does not require deciphering an env value, then just copy it
  107:      */
  108:     if (*pzDir != '$')
  109:         return pzDir;
  110: 
  111:     {
  112:         tCC*  pzEndDir = strchr(++pzDir, DIRCH);
  113:         char* pzFileName;
  114:         char* pzEnv;
  115: 
  116:         if (pzEndDir != NULL) {
  117:             char z[ AO_NAME_SIZE ];
  118:             if ((pzEndDir - pzDir) > AO_NAME_LIMIT )
  119:                 return NULL;
  120:             memcpy(z, pzDir, (size_t)(pzEndDir - pzDir));
  121:             z[pzEndDir - pzDir] = NUL;
  122:             pzEnv = getenv(z);
  123:         } else {
  124: 
  125:             /*
  126:              *  Make sure we can get the env value (after stripping off
  127:              *  any trailing directory or file names)
  128:              */
  129:             pzEnv = getenv(pzDir);
  130:         }
  131: 
  132:         if (pzEnv == NULL) {
  133:             fprintf(stderr, zWarn, pOpts->pzProgName);
  134:             fprintf(stderr, zNotDef, pzDir);
  135:             return NULL;
  136:         }
  137: 
  138:         if (pzEndDir == NULL)
  139:             return pzEnv;
  140: 
  141:         {
  142:             size_t sz = strlen(pzEnv) + strlen(pzEndDir) + 2;
  143:             pzFileName = (char*)AGALOC(sz, "dir name");
  144:         }
  145: 
  146:         if (pzFileName == NULL)
  147:             return NULL;
  148: 
  149:         *p_free = 1;
  150:         /*
  151:          *  Glue together the full name into the allocated memory.
  152:          *  FIXME: We lose track of this memory.
  153:          */
  154:         sprintf(pzFileName, "%s/%s", pzEnv, pzEndDir);
  155:         return pzFileName;
  156:     }
  157: }
  158: 
  159: 
  160: static char const *
  161: findFileName(tOptions * pOpts, int * p_free_name)
  162: {
  163:     struct stat stBuf;
  164:     int    free_dir_name = 0;
  165: 
  166:     char const * pzDir = findDirName(pOpts, &free_dir_name);
  167:     if (pzDir == NULL)
  168:         return NULL;
  169: 
  170:     /*
  171:      *  See if we can find the specified directory.  We use a once-only loop
  172:      *  structure so we can bail out early.
  173:      */
  174:     if (stat(pzDir, &stBuf) != 0) do {
  175:         char z[AG_PATH_MAX];
  176:         char * dirchp;
  177: 
  178:         /*
  179:          *  IF we could not, check to see if we got a full
  180:          *  path to a file name that has not been created yet.
  181:          */
  182:         if (errno != ENOENT) {
  183:         bogus_name:
  184:             fprintf(stderr, zWarn, pOpts->pzProgName);
  185:             fprintf(stderr, zNoStat, errno, strerror(errno), pzDir);
  186:             if (free_dir_name)
  187:                 AGFREE((void*)pzDir);
  188:             return NULL;
  189:         }
  190: 
  191:         /*
  192:          *  Strip off the last component, stat the remaining string and
  193:          *  that string must name a directory
  194:          */
  195:         dirchp = strrchr(pzDir, DIRCH);
  196:         if (dirchp == NULL) {
  197:             stBuf.st_mode = S_IFREG;
  198:             break; /* found directory -- viz.,  "." */
  199:         }
  200: 
  201:         if ((dirchp - pzDir) >= sizeof(z))
  202:             goto bogus_name;
  203: 
  204:         memcpy(z, pzDir, (size_t)(dirchp - pzDir));
  205:         z[dirchp - pzDir] = NUL;
  206: 
  207:         if ((stat(z, &stBuf) != 0) || ! S_ISDIR(stBuf.st_mode))
  208:             goto bogus_name;
  209:         stBuf.st_mode = S_IFREG; /* file within this directory */
  210:     } while (0);
  211: 
  212:     /*
  213:      *  IF what we found was a directory,
  214:      *  THEN tack on the config file name
  215:      */
  216:     if (S_ISDIR(stBuf.st_mode)) {
  217:         size_t sz = strlen(pzDir) + strlen(pOpts->pzRcName) + 2;
  218: 
  219:         {
  220:             char*  pzPath = (char*)AGALOC(sz, "file name");
  221: #ifdef HAVE_SNPRINTF
  222:             snprintf(pzPath, sz, "%s/%s", pzDir, pOpts->pzRcName);
  223: #else
  224:             sprintf(pzPath, "%s/%s", pzDir, pOpts->pzRcName);
  225: #endif
  226:             if (free_dir_name)
  227:                 AGFREE((void*)pzDir);
  228:             pzDir = pzPath;
  229:             free_dir_name = 1;
  230:         }
  231: 
  232:         /*
  233:          *  IF we cannot stat the object for any reason other than
  234:          *     it does not exist, then we bail out
  235:          */
  236:         if (stat(pzDir, &stBuf) != 0) {
  237:             if (errno != ENOENT) {
  238:                 fprintf(stderr, zWarn, pOpts->pzProgName);
  239:                 fprintf(stderr, zNoStat, errno, strerror(errno),
  240:                         pzDir);
  241:                 AGFREE((void*)pzDir);
  242:                 return NULL;
  243:             }
  244: 
  245:             /*
  246:              *  It does not exist yet, but it will be a regular file
  247:              */
  248:             stBuf.st_mode = S_IFREG;
  249:         }
  250:     }
  251: 
  252:     /*
  253:      *  Make sure that whatever we ultimately found, that it either is
  254:      *  or will soon be a file.
  255:      */
  256:     if (! S_ISREG(stBuf.st_mode)) {
  257:         fprintf(stderr, zWarn, pOpts->pzProgName);
  258:         fprintf(stderr, zNotFile, pzDir);
  259:         if (free_dir_name)
  260:             AGFREE((void*)pzDir);
  261:         return NULL;
  262:     }
  263: 
  264:     /*
  265:      *  Get rid of the old file
  266:      */
  267:     unlink(pzDir);
  268:     *p_free_name = free_dir_name;
  269:     return pzDir;
  270: }
  271: 
  272: 
  273: static void
  274: printEntry(
  275:     FILE *     fp,
  276:     tOptDesc * p,
  277:     tCC*       pzLA )
  278: {
  279:     /*
  280:      *  There is an argument.  Pad the name so values line up.
  281:      *  Not disabled *OR* this got equivalenced to another opt,
  282:      *  then use current option name.
  283:      *  Otherwise, there must be a disablement name.
  284:      */
  285:     {
  286:         char const * pz;
  287:         if (! DISABLED_OPT(p) || (p->optEquivIndex != NO_EQUIVALENT))
  288:             pz = p->pz_Name;
  289:         else
  290:             pz = p->pz_DisableName;
  291: 
  292:         fprintf(fp, "%-18s", pz);
  293:     }
  294:     /*
  295:      *  IF the option is numeric only,
  296:      *  THEN the char pointer is really the number
  297:      */
  298:     if (OPTST_GET_ARGTYPE(p->fOptState) == OPARG_TYPE_NUMERIC)
  299:         fprintf(fp, "  %d\n", (int)(t_word)pzLA);
  300: 
  301:     /*
  302:      *  OTHERWISE, FOR each line of the value text, ...
  303:      */
  304:     else if (pzLA == NULL)
  305:         fputc('\n', fp);
  306: 
  307:     else {
  308:         fputc(' ', fp); fputc(' ', fp);
  309:         for (;;) {
  310:             tCC* pzNl = strchr(pzLA, '\n');
  311: 
  312:             /*
  313:              *  IF this is the last line
  314:              *  THEN bail and print it
  315:              */
  316:             if (pzNl == NULL)
  317:                 break;
  318: 
  319:             /*
  320:              *  Print the continuation and the text from the current line
  321:              */
  322:             (void)fwrite(pzLA, (size_t)(pzNl - pzLA), (size_t)1, fp);
  323:             pzLA = pzNl+1; /* advance the Last Arg pointer */
  324:             fputs("\\\n", fp);
  325:         }
  326: 
  327:         /*
  328:          *  Terminate the entry
  329:          */
  330:         fputs(pzLA, fp);
  331:         fputc('\n', fp);
  332:     }
  333: }
  334: 
  335: 
  336: static void
  337: print_a_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp)
  338: {
  339:     static char const bool_atr[]  = "<%1$s type=boolean>%2$s</%1$s>\n";
  340:     static char const numb_atr[]  = "<%1$s type=integer>0x%2$lX</%1$s>\n";
  341:     static char const type_atr[]  = "<%s type=%s>";
  342:     static char const null_atr[]  = "<%s/>\n";
  343: 
  344:     while (--depth >= 0)
  345:         putc(' ', fp), putc(' ', fp);
  346: 
  347:     switch (ovp->valType) {
  348:     default:
  349:     case OPARG_TYPE_NONE:
  350:         fprintf(fp, null_atr, ovp->pzName);
  351:         break;
  352: 
  353:     case OPARG_TYPE_STRING:
  354:         print_a_string(fp, ovp->pzName, ovp->v.strVal);
  355:         break;
  356: 
  357:     case OPARG_TYPE_ENUMERATION:
  358:     case OPARG_TYPE_MEMBERSHIP:
  359:         if (pOD != NULL) {
  360:             tAoUI     opt_state = pOD->fOptState;
  361:             uintptr_t val = pOD->optArg.argEnum;
  362:             char const * typ = (ovp->valType == OPARG_TYPE_ENUMERATION)
  363:                 ? "keyword" : "set-membership";
  364: 
  365:             fprintf(fp, type_atr, ovp->pzName, typ);
  366: 
  367:             /*
  368:              *  This is a magic incantation that will convert the
  369:              *  bit flag values back into a string suitable for printing.
  370:              */
  371:             (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD );
  372:             if (pOD->optArg.argString != NULL) {
  373:                 fputs(pOD->optArg.argString, fp);
  374: 
  375:                 if (ovp->valType != OPARG_TYPE_ENUMERATION) {
  376:                     /*
  377:                      *  set membership strings get allocated
  378:                      */
  379:                     AGFREE((void*)pOD->optArg.argString);
  380:                 }
  381:             }
  382: 
  383:             pOD->optArg.argEnum = val;
  384:             pOD->fOptState = opt_state;
  385:             fprintf(fp, close_xml, ovp->pzName);
  386:             break;
  387:         }
  388:         /* FALLTHROUGH */
  389: 
  390:     case OPARG_TYPE_NUMERIC:
  391:         fprintf(fp, numb_atr, ovp->pzName, ovp->v.longVal);
  392:         break;
  393: 
  394:     case OPARG_TYPE_BOOLEAN:
  395:         fprintf(fp, bool_atr, ovp->pzName,
  396:                 ovp->v.boolVal ? "true" : "false");
  397:         break;
  398: 
  399:     case OPARG_TYPE_HIERARCHY:
  400:         printValueList(fp, ovp->pzName, ovp->v.nestVal);
  401:         break;
  402:     }
  403: }
  404: 
  405: 
  406: static void
  407: print_a_string(FILE * fp, char const * name, char const * pz)
  408: {
  409:     static char const open_atr[]  = "<%s>";
  410: 
  411:     fprintf(fp, open_atr, name);
  412:     for (;;) {
  413:         int ch = ((int)*(pz++)) & 0xFF;
  414: 
  415:         switch (ch) {
  416:         case NUL: goto string_done;
  417: 
  418:         case '&':
  419:         case '<':
  420:         case '>':
  421: #if __GNUC__ >= 4
  422:         case 1 ... (' ' - 1):
  423:         case ('~' + 1) ... 0xFF:
  424: #endif
  425:             emit_special_char(fp, ch);
  426:             break;
  427: 
  428:         default:
  429: #if __GNUC__ < 4
  430:             if (  ((ch >= 1) && (ch <= (' ' - 1)))
  431:                || ((ch >= ('~' + 1)) && (ch <= 0xFF)) ) {
  432:                 emit_special_char(fp, ch);
  433:                 break;
  434:             }
  435: #endif
  436:             putc(ch, fp);
  437:         }
  438:     } string_done:;
  439:     fprintf(fp, close_xml, name);
  440: }
  441: 
  442: 
  443: static void
  444: printValueList(FILE * fp, char const * name, tArgList * al)
  445: {
  446:     static int depth = 1;
  447: 
  448:     int sp_ct;
  449:     int opt_ct;
  450:     void ** opt_list;
  451: 
  452:     if (al == NULL)
  453:         return;
  454:     opt_ct   = al->useCt;
  455:     opt_list = (void **)al->apzArgs;
  456: 
  457:     if (opt_ct <= 0) {
  458:         fprintf(fp, "<%s/>\n", name);
  459:         return;
  460:     }
  461: 
  462:     fprintf(fp, "<%s type=nested>\n", name);
  463: 
  464:     depth++;
  465:     while (--opt_ct >= 0) {
  466:         tOptionValue const * ovp = *(opt_list++);
  467: 
  468:         print_a_value(fp, depth, NULL, ovp);
  469:     }
  470:     depth--;
  471: 
  472:     for (sp_ct = depth; --sp_ct >= 0;)
  473:         putc(' ', fp), putc(' ', fp);
  474:     fprintf(fp, "</%s>\n", name);
  475: }
  476: 
  477: 
  478: static void
  479: printHierarchy(FILE * fp, tOptDesc * p)
  480: {
  481:     int opt_ct;
  482:     tArgList * al = p->optCookie;
  483:     void ** opt_list;
  484: 
  485:     if (al == NULL)
  486:         return;
  487: 
  488:     opt_ct   = al->useCt;
  489:     opt_list = (void **)al->apzArgs;
  490: 
  491:     if (opt_ct <= 0)
  492:         return;
  493: 
  494:     do  {
  495:         tOptionValue const * base = *(opt_list++);
  496:         tOptionValue const * ovp = optionGetValue(base, NULL);
  497: 
  498:         if (ovp == NULL)
  499:             continue;
  500: 
  501:         fprintf(fp, "<%s type=nested>\n", p->pz_Name);
  502: 
  503:         do  {
  504:             print_a_value(fp, 1, p, ovp);
  505: 
  506:         } while (ovp = optionNextValue(base, ovp),
  507:                  ovp != NULL);
  508: 
  509:         fprintf(fp, "</%s>\n", p->pz_Name);
  510:     } while (--opt_ct > 0);
  511: }
  512: 
  513: 
  514: static FILE *
  515: openSaveFile(tOptions* pOpts)
  516: {
  517:     FILE*     fp;
  518: 
  519:     {
  520:         int   free_name = 0;
  521:         tCC*  pzFName = findFileName(pOpts, &free_name);
  522:         if (pzFName == NULL)
  523:             return NULL;
  524: 
  525:         fp = fopen(pzFName, "w" FOPEN_BINARY_FLAG);
  526:         if (fp == NULL) {
  527:             fprintf(stderr, zWarn, pOpts->pzProgName);
  528:             fprintf(stderr, zNoCreat, errno, strerror(errno), pzFName);
  529:             if (free_name)
  530:                 AGFREE((void*) pzFName );
  531:             return fp;
  532:         }
  533: 
  534:         if (free_name)
  535:             AGFREE((void*)pzFName);
  536:     }
  537: 
  538:     {
  539:         char const*  pz = pOpts->pzUsageTitle;
  540:         fputs("#  ", fp);
  541:         do { fputc(*pz, fp); } while (*(pz++) != '\n');
  542:     }
  543: 
  544:     {
  545:         time_t  timeVal = time(NULL);
  546:         char*   pzTime  = ctime(&timeVal);
  547: 
  548:         fprintf(fp, zPresetFile, pzTime);
  549: #ifdef HAVE_ALLOCATED_CTIME
  550:         /*
  551:          *  The return values for ctime(), localtime(), and gmtime()
  552:          *  normally point to static data that is overwritten by each call.
  553:          *  The test to detect allocated ctime, so we leak the memory.
  554:          */
  555:         AGFREE((void*)pzTime);
  556: #endif
  557:     }
  558: 
  559:     return fp;
  560: }
  561: 
  562: static void
  563: printNoArgOpt(FILE * fp, tOptDesc * p, tOptDesc * pOD)
  564: {
  565:     /*
  566:      * The aliased to argument indicates whether or not the option
  567:      * is "disabled".  However, the original option has the name
  568:      * string, so we get that there, not with "p".
  569:      */
  570:     char const * pznm =
  571:         (DISABLED_OPT(p)) ? pOD->pz_DisableName : pOD->pz_Name;
  572:     /*
  573:      *  If the option was disabled and the disablement name is NULL,
  574:      *  then the disablement was caused by aliasing.
  575:      *  Use the name as the string to emit.
  576:      */
  577:     if (pznm == NULL)
  578:         pznm = pOD->pz_Name;
  579: 
  580:     fprintf(fp, "%s\n", pznm);
  581: }
  582: 
  583: static void
  584: printStringArg(FILE * fp, tOptDesc * pOD)
  585: {
  586:     if (pOD->fOptState & OPTST_STACKED) {
  587:         tArgList*  pAL = (tArgList*)pOD->optCookie;
  588:         int        uct = pAL->useCt;
  589:         tCC**      ppz = pAL->apzArgs;
  590: 
  591:         /*
  592:          *  un-disable multiple copies of disabled options.
  593:          */
  594:         if (uct > 1)
  595:             pOD->fOptState &= ~OPTST_DISABLED;
  596: 
  597:         while (uct-- > 0)
  598:             printEntry(fp, pOD, *(ppz++));
  599:     } else {
  600:         printEntry(fp, pOD, pOD->optArg.argString);
  601:     }
  602: }
  603: 
  604: static void
  605: printEnumArg(FILE * fp, tOptDesc * pOD)
  606: {
  607:     uintptr_t val = pOD->optArg.argEnum;
  608: 
  609:     /*
  610:      *  This is a magic incantation that will convert the
  611:      *  bit flag values back into a string suitable for printing.
  612:      */
  613:     (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
  614:     printEntry(fp, pOD, (void*)(pOD->optArg.argString));
  615: 
  616:     pOD->optArg.argEnum = val;
  617: }
  618: 
  619: static void
  620: printSetMemberArg(FILE * fp, tOptDesc * pOD)
  621: {
  622:     uintptr_t val = pOD->optArg.argEnum;
  623: 
  624:     /*
  625:      *  This is a magic incantation that will convert the
  626:      *  bit flag values back into a string suitable for printing.
  627:      */
  628:     (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
  629:     printEntry(fp, pOD, (void*)(pOD->optArg.argString));
  630: 
  631:     if (pOD->optArg.argString != NULL) {
  632:         /*
  633:          *  set membership strings get allocated
  634:          */
  635:         AGFREE((void*)pOD->optArg.argString);
  636:         pOD->fOptState &= ~OPTST_ALLOC_ARG;
  637:     }
  638: 
  639:     pOD->optArg.argEnum = val;
  640: }
  641: 
  642: static void
  643: printFileArg(FILE * fp, tOptDesc * pOD, tOptions* pOpts)
  644: {
  645:     /*
  646:      *  If the cookie is not NULL, then it has the file name, period.
  647:      *  Otherwise, if we have a non-NULL string argument, then....
  648:      */
  649:     if (pOD->optCookie != NULL)
  650:         printEntry(fp, pOD, pOD->optCookie);
  651: 
  652:     else if (HAS_originalOptArgArray(pOpts)) {
  653:         char const * orig =
  654:             pOpts->originalOptArgArray[pOD->optIndex].argString;
  655: 
  656:         if (pOD->optArg.argString == orig)
  657:             return;
  658: 
  659:         printEntry(fp, pOD, pOD->optArg.argString);
  660:     }
  661: }
  662: 
  663: 
  664: /*=export_func  optionSaveFile
  665:  *
  666:  * what:  saves the option state to a file
  667:  *
  668:  * arg:   tOptions*,   pOpts,  program options descriptor
  669:  *
  670:  * doc:
  671:  *
  672:  * This routine will save the state of option processing to a file.  The name
  673:  * of that file can be specified with the argument to the @code{--save-opts}
  674:  * option, or by appending the @code{rcfile} attribute to the last
  675:  * @code{homerc} attribute.  If no @code{rcfile} attribute was specified, it
  676:  * will default to @code{.@i{programname}rc}.  If you wish to specify another
  677:  * file, you should invoke the @code{SET_OPT_SAVE_OPTS(@i{filename})} macro.
  678:  *
  679:  * The recommend usage is as follows:
  680:  * @example
  681:  *    optionProcess(&progOptions, argc, argv);
  682:  *    if (i_want_a_non_standard_place_for_this)
  683:  *        SET_OPT_SAVE_OPTS("myfilename");
  684:  *    optionSaveFile(&progOptions);
  685:  * @end example
  686:  *
  687:  * err:
  688:  *
  689:  * If no @code{homerc} file was specified, this routine will silently return
  690:  * and do nothing.  If the output file cannot be created or updated, a message
  691:  * will be printed to @code{stderr} and the routine will return.
  692: =*/
  693: void
  694: optionSaveFile(tOptions* pOpts)
  695: {
  696:     tOptDesc* pOD;
  697:     int       ct;
  698:     FILE*     fp = openSaveFile(pOpts);
  699: 
  700:     if (fp == NULL)
  701:         return;
  702: 
  703:     /*
  704:      *  FOR each of the defined options, ...
  705:      */
  706:     ct  = pOpts->presetOptCt;
  707:     pOD = pOpts->pOptDesc;
  708:     do  {
  709:         tOptDesc*  p;
  710: 
  711:         /*
  712:          *  IF    the option has not been defined
  713:          *     OR it does not take an initialization value
  714:          *     OR it is equivalenced to another option
  715:          *  THEN continue (ignore it)
  716:          *
  717:          *  Equivalenced options get picked up when the equivalenced-to
  718:          *  option is processed.
  719:          */
  720:         if (UNUSED_OPT(pOD))
  721:             continue;
  722: 
  723:         if ((pOD->fOptState & OPTST_DO_NOT_SAVE_MASK) != 0)
  724:             continue;
  725: 
  726:         if (  (pOD->optEquivIndex != NO_EQUIVALENT)
  727:            && (pOD->optEquivIndex != pOD->optIndex))
  728:             continue;
  729: 
  730:         /*
  731:          *  The option argument data are found at the equivalenced-to option,
  732:          *  but the actual option argument type comes from the original
  733:          *  option descriptor.  Be careful!
  734:          */
  735:         p = ((pOD->fOptState & OPTST_EQUIVALENCE) != 0)
  736:             ? (pOpts->pOptDesc + pOD->optActualIndex) : pOD;
  737: 
  738:         switch (OPTST_GET_ARGTYPE(pOD->fOptState)) {
  739:         case OPARG_TYPE_NONE:
  740:             printNoArgOpt(fp, p, pOD);
  741:             break;
  742: 
  743:         case OPARG_TYPE_NUMERIC:
  744:             printEntry(fp, p, (void*)(p->optArg.argInt));
  745:             break;
  746: 
  747:         case OPARG_TYPE_STRING:
  748:             printStringArg(fp, p);
  749:             break;
  750: 
  751:         case OPARG_TYPE_ENUMERATION:
  752:             printEnumArg(fp, p);
  753:             break;
  754: 
  755:         case OPARG_TYPE_MEMBERSHIP:
  756:             printSetMemberArg(fp, p);
  757:             break;
  758: 
  759:         case OPARG_TYPE_BOOLEAN:
  760:             printEntry(fp, p, p->optArg.argBool ? "true" : "false");
  761:             break;
  762: 
  763:         case OPARG_TYPE_HIERARCHY:
  764:             printHierarchy(fp, p);
  765:             break;
  766: 
  767:         case OPARG_TYPE_FILE:
  768:             printFileArg(fp, p, pOpts);
  769:             break;
  770: 
  771:         default:
  772:             break; /* cannot handle - skip it */
  773:         }
  774:     } while (pOD++, (--ct > 0));
  775: 
  776:     fclose(fp);
  777: }
  778: /*
  779:  * Local Variables:
  780:  * mode: C
  781:  * c-file-style: "stroustrup"
  782:  * indent-tabs-mode: nil
  783:  * End:
  784:  * end of autoopts/save.c */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>