static char const zWarn[] = "%s WARNING: cannot save options - ";
static char const close_xml[] = "</%s>\n";
static tCC*
findDirName( tOptions* pOpts, int* p_free );
static tCC*
findFileName( tOptions* pOpts, int* p_free_name );
static void
printEntry(
FILE * fp,
tOptDesc * p,
tCC* pzLA );
static void
print_a_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp);
static void
print_a_string(FILE * fp, char const * name, char const * pz);
static void
printValueList(FILE * fp, char const * name, tArgList * al);
static void
printHierarchy(FILE * fp, tOptDesc * p);
static FILE *
openSaveFile( tOptions* pOpts );
static void
printNoArgOpt(FILE * fp, tOptDesc * p, tOptDesc * pOD);
static void
printStringArg(FILE * fp, tOptDesc * pOD);
static void
printEnumArg(FILE * fp, tOptDesc * pOD);
static void
printSetMemberArg(FILE * fp, tOptDesc * pOD);
static void
printFileArg(FILE * fp, tOptDesc * pOD, tOptions* pOpts);
static tCC*
findDirName( tOptions* pOpts, int* p_free )
{
tCC* pzDir;
if ( (pOpts->specOptIdx.save_opts == NO_EQUIVALENT)
|| (pOpts->specOptIdx.save_opts == 0))
return NULL;
pzDir = pOpts->pOptDesc[ pOpts->specOptIdx.save_opts ].optArg.argString;
if ((pzDir != NULL) && (*pzDir != NUL))
return pzDir;
{
tCC* const* papz = pOpts->papzHomeList;
if (papz == NULL)
return NULL;
while (papz[1] != NULL) papz++;
pzDir = *papz;
}
if (*pzDir != '$')
return pzDir;
{
tCC* pzEndDir = strchr( ++pzDir, DIRCH );
char* pzFileName;
char* pzEnv;
if (pzEndDir != NULL) {
char z[ AO_NAME_SIZE ];
if ((pzEndDir - pzDir) > AO_NAME_LIMIT )
return NULL;
strncpy( z, pzDir, (size_t)(pzEndDir - pzDir) );
z[ (pzEndDir - pzDir) ] = NUL;
pzEnv = getenv( z );
} else {
pzEnv = getenv( pzDir );
}
if (pzEnv == NULL) {
fprintf( stderr, zWarn, pOpts->pzProgName );
fprintf( stderr, zNotDef, pzDir );
return NULL;
}
if (pzEndDir == NULL)
return pzEnv;
{
size_t sz = strlen( pzEnv ) + strlen( pzEndDir ) + 2;
pzFileName = (char*)AGALOC( sz, "dir name" );
}
if (pzFileName == NULL)
return NULL;
*p_free = 1;
sprintf( pzFileName, "%s/%s", pzEnv, pzEndDir );
return pzFileName;
}
}
static tCC*
findFileName( tOptions* pOpts, int* p_free_name )
{
tCC* pzDir;
struct stat stBuf;
int free_dir_name = 0;
pzDir = findDirName( pOpts, &free_dir_name );
if (pzDir == NULL)
return NULL;
if (stat( pzDir, &stBuf ) != 0) do {
if (errno == ENOENT) {
char z[AG_PATH_MAX];
char* pzDirCh = strrchr( pzDir, DIRCH );
if (pzDirCh == NULL) {
stBuf.st_mode = S_IFREG;
continue;
}
strncpy( z, pzDir, (size_t)(pzDirCh - pzDir));
z[ pzDirCh - pzDir ] = NUL;
if ( (stat( z, &stBuf ) == 0)
&& S_ISDIR( stBuf.st_mode )) {
stBuf.st_mode = S_IFREG;
continue;
}
}
fprintf( stderr, zWarn, pOpts->pzProgName );
fprintf( stderr, zNoStat, errno, strerror( errno ), pzDir );
if (free_dir_name)
AGFREE( (void*)pzDir );
return NULL;
} while (0);
if (S_ISDIR( stBuf.st_mode )) {
size_t sz = strlen( pzDir ) + strlen( pOpts->pzRcName ) + 2;
{
char* pzPath = (char*)AGALOC( sz, "file name" );
#ifdef HAVE_SNPRINTF
snprintf( pzPath, sz, "%s/%s", pzDir, pOpts->pzRcName );
#else
sprintf( pzPath, "%s/%s", pzDir, pOpts->pzRcName );
#endif
if (free_dir_name)
AGFREE( (void*)pzDir );
pzDir = pzPath;
free_dir_name = 1;
}
if (stat( pzDir, &stBuf ) != 0) {
if (errno != ENOENT) {
fprintf( stderr, zWarn, pOpts->pzProgName );
fprintf( stderr, zNoStat, errno, strerror( errno ),
pzDir );
AGFREE( (void*)pzDir );
return NULL;
}
stBuf.st_mode = S_IFREG;
}
}
if (! S_ISREG( stBuf.st_mode )) {
fprintf( stderr, zWarn, pOpts->pzProgName );
fprintf( stderr, zNotFile, pzDir );
if (free_dir_name)
AGFREE( (void*)pzDir );
return NULL;
}
unlink( pzDir );
*p_free_name = free_dir_name;
return pzDir;
}
static void
printEntry(
FILE * fp,
tOptDesc * p,
tCC* pzLA )
{
{
char const * pz;
if (! DISABLED_OPT(p) || (p->optEquivIndex != NO_EQUIVALENT))
pz = p->pz_Name;
else
pz = p->pz_DisableName;
fprintf(fp, "%-18s", pz);
}
if (OPTST_GET_ARGTYPE(p->fOptState) == OPARG_TYPE_NUMERIC)
fprintf( fp, " %d\n", (int)(t_word)pzLA );
else if (pzLA == NULL)
fputc( '\n', fp );
else {
fputc( ' ', fp ); fputc( ' ', fp );
for (;;) {
tCC* pzNl = strchr( pzLA, '\n' );
if (pzNl == NULL)
break;
(void)fwrite( pzLA, (size_t)(pzNl - pzLA), (size_t)1, fp );
pzLA = pzNl+1;
fputs( "\\\n", fp );
}
fputs( pzLA, fp );
fputc( '\n', fp );
}
}
static void
print_a_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp)
{
static char const bool_atr[] = "<%1$s type=boolean>%2$s</%1$s>\n";
static char const numb_atr[] = "<%1$s type=integer>0x%2$lX</%1$s>\n";
static char const type_atr[] = "<%s type=%s>";
static char const null_atr[] = "<%s/>\n";
while (--depth >= 0)
putc(' ', fp), putc(' ', fp);
switch (ovp->valType) {
default:
case OPARG_TYPE_NONE:
fprintf(fp, null_atr, ovp->pzName);
break;
case OPARG_TYPE_STRING:
print_a_string(fp, ovp->pzName, ovp->v.strVal);
break;
case OPARG_TYPE_ENUMERATION:
case OPARG_TYPE_MEMBERSHIP:
if (pOD != NULL) {
tAoUI opt_state = pOD->fOptState;
uintptr_t val = pOD->optArg.argEnum;
char const * typ = (ovp->valType == OPARG_TYPE_ENUMERATION)
? "keyword" : "set-membership";
fprintf(fp, type_atr, ovp->pzName, typ);
(*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD );
if (pOD->optArg.argString != NULL) {
fputs(pOD->optArg.argString, fp);
if (ovp->valType != OPARG_TYPE_ENUMERATION) {
AGFREE( (void*)pOD->optArg.argString );
}
}
pOD->optArg.argEnum = val;
pOD->fOptState = opt_state;
fprintf(fp, close_xml, ovp->pzName);
break;
}
case OPARG_TYPE_NUMERIC:
fprintf(fp, numb_atr, ovp->pzName, ovp->v.longVal);
break;
case OPARG_TYPE_BOOLEAN:
fprintf(fp, bool_atr, ovp->pzName,
ovp->v.boolVal ? "true" : "false");
break;
case OPARG_TYPE_HIERARCHY:
printValueList(fp, ovp->pzName, ovp->v.nestVal);
break;
}
}
static void
print_a_string(FILE * fp, char const * name, char const * pz)
{
static char const open_atr[] = "<%s>";
fprintf(fp, open_atr, name);
for (;;) {
int ch = ((int)*(pz++)) & 0xFF;
switch (ch) {
case NUL: goto string_done;
case '&':
case '<':
case '>':
#if __GNUC__ >= 4
case 1 ... (' ' - 1):
case ('~' + 1) ... 0xFF:
#endif
emit_special_char(fp, ch);
break;
default:
#if __GNUC__ < 4
if ( ((ch >= 1) && (ch <= (' ' - 1)))
|| ((ch >= ('~' + 1)) && (ch <= 0xFF)) ) {
emit_special_char(fp, ch);
break;
}
#endif
putc(ch, fp);
}
} string_done:;
fprintf(fp, close_xml, name);
}
static void
printValueList(FILE * fp, char const * name, tArgList * al)
{
static int depth = 1;
int sp_ct;
int opt_ct;
void ** opt_list;
if (al == NULL)
return;
opt_ct = al->useCt;
opt_list = (void **)al->apzArgs;
if (opt_ct <= 0) {
fprintf(fp, "<%s/>\n", name);
return;
}
fprintf(fp, "<%s type=nested>\n", name);
depth++;
while (--opt_ct >= 0) {
tOptionValue const * ovp = *(opt_list++);
print_a_value(fp, depth, NULL, ovp);
}
depth--;
for (sp_ct = depth; --sp_ct >= 0;)
putc(' ', fp), putc(' ', fp);
fprintf(fp, "</%s>\n", name);
}
static void
printHierarchy(FILE * fp, tOptDesc * p)
{
int opt_ct;
tArgList * al = p->optCookie;
void ** opt_list;
if (al == NULL)
return;
opt_ct = al->useCt;
opt_list = (void **)al->apzArgs;
if (opt_ct <= 0)
return;
do {
tOptionValue const * base = *(opt_list++);
tOptionValue const * ovp = optionGetValue(base, NULL);
if (ovp == NULL)
continue;
fprintf(fp, "<%s type=nested>\n", p->pz_Name);
do {
print_a_value(fp, 1, p, ovp);
} while (ovp = optionNextValue(base, ovp),
ovp != NULL);
fprintf(fp, "</%s>\n", p->pz_Name);
} while (--opt_ct > 0);
}
static FILE *
openSaveFile( tOptions* pOpts )
{
FILE* fp;
{
int free_name = 0;
tCC* pzFName = findFileName( pOpts, &free_name );
if (pzFName == NULL)
return NULL;
fp = fopen( pzFName, "w" FOPEN_BINARY_FLAG );
if (fp == NULL) {
fprintf( stderr, zWarn, pOpts->pzProgName );
fprintf( stderr, zNoCreat, errno, strerror( errno ), pzFName );
if (free_name)
AGFREE((void*) pzFName );
return fp;
}
if (free_name)
AGFREE( (void*)pzFName );
}
{
char const* pz = pOpts->pzUsageTitle;
fputs( "# ", fp );
do { fputc( *pz, fp ); } while (*(pz++) != '\n');
}
{
time_t timeVal = time( NULL );
char* pzTime = ctime( &timeVal );
fprintf( fp, zPresetFile, pzTime );
#ifdef HAVE_ALLOCATED_CTIME
AGFREE( (void*)pzTime );
#endif
}
return fp;
}
static void
printNoArgOpt(FILE * fp, tOptDesc * p, tOptDesc * pOD)
{
char const * pznm =
(DISABLED_OPT( p )) ? pOD->pz_DisableName : pOD->pz_Name;
if (pznm == NULL)
pznm = pOD->pz_Name;
fprintf(fp, "%s\n", pznm);
}
static void
printStringArg(FILE * fp, tOptDesc * pOD)
{
if (pOD->fOptState & OPTST_STACKED) {
tArgList* pAL = (tArgList*)pOD->optCookie;
int uct = pAL->useCt;
tCC** ppz = pAL->apzArgs;
if (uct > 1)
pOD->fOptState &= ~OPTST_DISABLED;
while (uct-- > 0)
printEntry( fp, pOD, *(ppz++) );
} else {
printEntry( fp, pOD, pOD->optArg.argString );
}
}
static void
printEnumArg(FILE * fp, tOptDesc * pOD)
{
uintptr_t val = pOD->optArg.argEnum;
(*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
printEntry( fp, pOD, (void*)(pOD->optArg.argString));
pOD->optArg.argEnum = val;
}
static void
printSetMemberArg(FILE * fp, tOptDesc * pOD)
{
uintptr_t val = pOD->optArg.argEnum;
(*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
printEntry( fp, pOD, (void*)(pOD->optArg.argString));
if (pOD->optArg.argString != NULL) {
AGFREE( (void*)pOD->optArg.argString );
pOD->fOptState &= ~OPTST_ALLOC_ARG;
}
pOD->optArg.argEnum = val;
}
static void
printFileArg(FILE * fp, tOptDesc * pOD, tOptions* pOpts)
{
if (pOD->optCookie != NULL)
printEntry(fp, pOD, pOD->optCookie);
else if (HAS_originalOptArgArray(pOpts)) {
char const * orig =
pOpts->originalOptArgArray[pOD->optIndex].argString;
if (pOD->optArg.argString == orig)
return;
printEntry(fp, pOD, pOD->optArg.argString);
}
}
void
optionSaveFile( tOptions* pOpts )
{
tOptDesc* pOD;
int ct;
FILE* fp = openSaveFile(pOpts);
if (fp == NULL)
return;
ct = pOpts->presetOptCt;
pOD = pOpts->pOptDesc;
do {
tOptDesc* p;
if (UNUSED_OPT( pOD ))
continue;
if ((pOD->fOptState & OPTST_DO_NOT_SAVE_MASK) != 0)
continue;
if ( (pOD->optEquivIndex != NO_EQUIVALENT)
&& (pOD->optEquivIndex != pOD->optIndex))
continue;
p = ((pOD->fOptState & OPTST_EQUIVALENCE) != 0)
? (pOpts->pOptDesc + pOD->optActualIndex) : pOD;
switch (OPTST_GET_ARGTYPE(pOD->fOptState)) {
case OPARG_TYPE_NONE:
printNoArgOpt(fp, p, pOD);
break;
case OPARG_TYPE_NUMERIC:
printEntry( fp, p, (void*)(p->optArg.argInt));
break;
case OPARG_TYPE_STRING:
printStringArg(fp, p);
break;
case OPARG_TYPE_ENUMERATION:
printEnumArg(fp, p);
break;
case OPARG_TYPE_MEMBERSHIP:
printSetMemberArg(fp, p);
break;
case OPARG_TYPE_BOOLEAN:
printEntry( fp, p, p->optArg.argBool ? "true" : "false" );
break;
case OPARG_TYPE_HIERARCHY:
printHierarchy(fp, p);
break;
case OPARG_TYPE_FILE:
printFileArg(fp, p, pOpts);
break;
default:
break;
}
} while ( (pOD++), (--ct > 0));
fclose( fp );
}