1535 lines
39 KiB
C++
1535 lines
39 KiB
C++
/*******************************************************************************
|
|
* processoptions.cpp
|
|
*
|
|
* This module contains the C++ interface for option processing.
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
|
|
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
|
|
*
|
|
* POV-Ray is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* POV-Ray 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 Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------------
|
|
* POV-Ray is based on the popular DKB raytracer version 2.12.
|
|
* DKBTrace was originally written by David K. Buck.
|
|
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
|
|
* ---------------------------------------------------------------------------
|
|
* $File: //depot/public/povray/3.x/source/base/processoptions.cpp $
|
|
* $Revision: #1 $
|
|
* $Change: 6069 $
|
|
* $DateTime: 2013/11/06 11:59:40 $
|
|
* $Author: chrisc $
|
|
*******************************************************************************/
|
|
|
|
#include <cstdarg>
|
|
#include <cctype>
|
|
|
|
// configbase.h must always be the first POV file included within base *.cpp files
|
|
#include "base/configbase.h"
|
|
#include "base/povmscpp.h"
|
|
#include "base/processoptions.h"
|
|
#include "base/stringutilities.h"
|
|
#include "base/povmsgid.h"
|
|
#include "base/pov_err.h"
|
|
|
|
// this must be the last file included
|
|
#include "base/povdebug.h"
|
|
|
|
namespace pov_base
|
|
{
|
|
|
|
ProcessOptions::ProcessOptions(INI_Parser_Table *pit, Cmd_Parser_Table *pct)
|
|
{
|
|
parse_ini_table = pit;
|
|
parse_cmd_table = pct;
|
|
}
|
|
|
|
ProcessOptions::~ProcessOptions()
|
|
{
|
|
}
|
|
|
|
int ProcessOptions::ParseFile(const char *filespec, POVMSObjectPtr obj)
|
|
{
|
|
ITextStream *file = NULL;
|
|
const char *currentsection = NULL;
|
|
char *sectionname = NULL;
|
|
char *filename = NULL;
|
|
int err = kNoErr;
|
|
POVMSObjectPtr section = NULL;
|
|
int currentline = 1;
|
|
|
|
// split the INI files specification into filename and optional section name
|
|
err = Parse_INI_Specification(filespec, filename, sectionname);
|
|
if(err == kNoErr)
|
|
{
|
|
file = OpenFileForRead(filename, obj);
|
|
if(file == NULL)
|
|
{
|
|
// all errors here are non-fatal, the calling code has to decide if an error is fatal
|
|
ParseError("Cannot open INI file '%s'.", filename);
|
|
err= kCannotOpenFileErr;
|
|
}
|
|
}
|
|
|
|
if(err == kNoErr)
|
|
{
|
|
int token = 0;
|
|
|
|
// apply options prior to any named section
|
|
section = obj;
|
|
|
|
// read the whole file to the end
|
|
while((file->eof() == false) && (err == kNoErr))
|
|
{
|
|
currentline += Parse_INI_Skip_Space(file, true);
|
|
|
|
token = file->getchar();
|
|
|
|
// INI file section name
|
|
if(token == '[')
|
|
{
|
|
// free old section name, if any
|
|
if(currentsection != NULL)
|
|
delete[] currentsection;
|
|
|
|
// read until the section name end marker
|
|
currentsection = Parse_INI_String(file, ']');
|
|
|
|
// if the user specified a section name, compare the two and enable reading
|
|
if((sectionname != NULL) && (currentsection != NULL))
|
|
{
|
|
if(pov_stricmp(currentsection, sectionname) == 0)
|
|
section = obj; // named section matches specified section name, apply options
|
|
else
|
|
section = NULL; // named section does not match specified section name, ignore options
|
|
}
|
|
// if there was no user specified section name, ignore all named sections
|
|
else
|
|
section = NULL; // no section name was specified, ignore options in named section
|
|
}
|
|
// skip lines that do not belong to the desired sections
|
|
else if(section == NULL)
|
|
{
|
|
currentline += Parse_INI_Skip_Line(file);
|
|
}
|
|
// fully process lines in desired sections
|
|
else
|
|
{
|
|
switch(token)
|
|
{
|
|
// end of file
|
|
case EOF:
|
|
break;
|
|
// quoted string
|
|
case '\"':
|
|
case '\'':
|
|
err = kFalseErr;
|
|
break;
|
|
// POV-Ray-style INI file with command-line switch
|
|
case '+':
|
|
case '-':
|
|
// POV-Ray-style INI file with system specific command-line switch on some systems (i.e. Windos)
|
|
#if(FILENAME_SEPARATOR != '/')
|
|
case '/':
|
|
#endif
|
|
err = Parse_INI_Switch(file, token, section);
|
|
break;
|
|
// INI file comment
|
|
case ';':
|
|
case '#':
|
|
currentline += Parse_INI_Skip_Line(file);
|
|
break;
|
|
// INI file option
|
|
default:
|
|
if(isalnum(token) || (token == '_'))
|
|
{
|
|
file->ungetchar(token);
|
|
ITextStream::FilePos backtrackpos = file->tellg();
|
|
err = Parse_INI_Option(file, section);
|
|
// only one option is allowed per line
|
|
if(err == kNoErr)
|
|
currentline += Parse_INI_Skip_Line(file);
|
|
else if(err == kParseErr)
|
|
{
|
|
file->seekg(backtrackpos);
|
|
err = kFalseErr;
|
|
}
|
|
}
|
|
else
|
|
err = kFalseErr;
|
|
break;
|
|
}
|
|
|
|
// if nothing else was appropriate, assume it is some other kind of string requiring special attention
|
|
if(err == kFalseErr)
|
|
{
|
|
char *plainstring = NULL;
|
|
|
|
if((token != '\"') && (token != '\''))
|
|
{
|
|
// if there were no quotes, just read up to the next space or newline
|
|
plainstring = Parse_INI_String(file, -2, true);
|
|
|
|
// see if it is probably a standard INI file entry which wasn't recognised
|
|
if(plainstring != NULL)
|
|
{
|
|
if(strchr(plainstring, '=') != NULL)
|
|
err = kParseErr;
|
|
}
|
|
}
|
|
else
|
|
plainstring = Parse_INI_String(file, token, true);
|
|
|
|
if(err == kFalseErr)
|
|
err = ProcessUnknownString(plainstring, obj);
|
|
|
|
if(plainstring != NULL)
|
|
delete[] plainstring;
|
|
}
|
|
}
|
|
}
|
|
|
|
// all errors here are non-fatal, the calling code has to decide if an error is fatal
|
|
if(err != kNoErr)
|
|
{
|
|
if(currentsection != NULL)
|
|
{
|
|
ParseErrorAt(file,
|
|
"Cannot continue to process INI file '%s' due to a parse error in line %d section '%s'.\n"
|
|
"This is not a valid INI file. Check the file for syntax errors, correct them, and try again!\n"
|
|
"Valid options in INI files are explained in detail in the reference part of the documentation.",
|
|
filename, currentline, currentsection);
|
|
}
|
|
else
|
|
{
|
|
ParseErrorAt(file,
|
|
"Cannot continue to process INI file '%s' due to a parse error in line %d.\n"
|
|
"This is not a valid INI file. Check the file for syntax errors, correct them, and try again!\n"
|
|
"Valid options in INI files are explained in detail in the reference part of the documentation.",
|
|
filename, currentline);
|
|
}
|
|
}
|
|
|
|
if(currentsection != NULL)
|
|
delete[] currentsection;
|
|
}
|
|
|
|
if(filename != NULL)
|
|
delete[] filename;
|
|
|
|
if(sectionname != NULL)
|
|
delete[] sectionname;
|
|
|
|
if(file != NULL)
|
|
delete file;
|
|
|
|
return err;
|
|
}
|
|
|
|
int ProcessOptions::ParseString(const char *commandline, POVMSObjectPtr obj, bool singleswitch)
|
|
{
|
|
int err = kNoErr;
|
|
|
|
// read the whole command-line to the end
|
|
while((*commandline != 0) && (err == kNoErr))
|
|
{
|
|
if(singleswitch == false) // see if quotes had been stripped outside POV-Ray
|
|
Parse_CL_Skip_Space(commandline);
|
|
|
|
switch(*commandline)
|
|
{
|
|
// end of command-line
|
|
case 0:
|
|
break;
|
|
// quoted string
|
|
case '\"':
|
|
case '\'':
|
|
err = kFalseErr;
|
|
break;
|
|
// switch
|
|
case '+':
|
|
case '-':
|
|
// system specific switch on some systems (i.e. Windos)
|
|
#if(FILENAME_SEPARATOR != '/')
|
|
case '/':
|
|
#endif
|
|
commandline++;
|
|
err = Parse_CL_Switch(commandline, *(commandline - 1), obj, singleswitch);
|
|
break;
|
|
// INI file style option
|
|
default:
|
|
if(isalnum(*commandline) || (*commandline == '_'))
|
|
{
|
|
const char *cltemp = commandline;
|
|
err = Parse_CL_Option(commandline, obj, singleswitch);
|
|
if(err == kParseErr)
|
|
{
|
|
commandline = cltemp;
|
|
err = kFalseErr;
|
|
}
|
|
}
|
|
else
|
|
err = kFalseErr;
|
|
break;
|
|
}
|
|
|
|
// if nothing else was appropriate, assume it is some other kind of string requiring special attention
|
|
if(err == kFalseErr)
|
|
{
|
|
int chr = *commandline;
|
|
char *plainstring = NULL;
|
|
|
|
if((chr == '\"') || (chr == '\''))
|
|
{
|
|
commandline++;
|
|
|
|
plainstring = Parse_CL_String(commandline, chr);
|
|
}
|
|
// if there were no quotes, just read up to the next space or newline
|
|
else if(singleswitch == false) // see if quotes had been stripped outside POV-Ray
|
|
{
|
|
const char *oldcmdline = commandline;
|
|
|
|
plainstring = Parse_CL_String(commandline, -2);
|
|
|
|
// check if it is probably a standard INI file entry which wasn't recognised
|
|
if((plainstring != NULL) && (strchr(plainstring, '=') != NULL))
|
|
{
|
|
err = kParseErr;
|
|
commandline = oldcmdline; // so it will be printed
|
|
}
|
|
}
|
|
else
|
|
plainstring = Parse_CL_String(commandline, 0);
|
|
|
|
if(err == kFalseErr)
|
|
err = ProcessUnknownString(plainstring, obj);
|
|
|
|
if(plainstring != NULL)
|
|
delete[] plainstring;
|
|
}
|
|
}
|
|
|
|
// all errors here are non-fatal, the calling code has to decide if an error is fatal
|
|
if(err != kNoErr)
|
|
{
|
|
if(*commandline != 0)
|
|
{
|
|
ParseError("Cannot process command-line at '%s' due to a parse error.\n"
|
|
"This is not a valid command-line. Check the command-line for syntax errors, correct them, and try again.\n"
|
|
"Valid command-line switches are explained in detail in the reference part of the documentation.",
|
|
commandline);
|
|
}
|
|
else
|
|
{
|
|
ParseError("Cannot process command-line due to a parse error.\n"
|
|
"This is not a valid command-line. Check the command-line for syntax errors, correct them, and try again.\n"
|
|
"Valid command-line switches are explained in detail in the reference part of the documentation.");
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int ProcessOptions::WriteFile(OTextStream *ini_file, POVMSObjectPtr obj)
|
|
{
|
|
struct INI_Parser_Table *table = parse_ini_table;
|
|
|
|
// find the keyword
|
|
while(table->keyword != NULL)
|
|
{
|
|
if(WriteOptionFilter(table) == true)
|
|
Output_INI_Option(table, obj, ini_file);
|
|
table++;
|
|
}
|
|
|
|
return kNoErr;
|
|
}
|
|
|
|
int ProcessOptions::WriteFile(const char *filename, POVMSObjectPtr obj)
|
|
{
|
|
OTextStream *ini_file;
|
|
int err = kNoErr;
|
|
|
|
if(!POV_ALLOW_FILE_WRITE(filename, POV_File_Text_INI))
|
|
return kCannotOpenFileErr;
|
|
|
|
ini_file = OpenFileForWrite(filename, obj);
|
|
if(ini_file == NULL)
|
|
return kCannotOpenFileErr;
|
|
err = WriteFile (ini_file, obj);
|
|
delete ini_file;
|
|
|
|
return err;
|
|
}
|
|
|
|
bool ProcessOptions::IsTrue(const char *value)
|
|
{
|
|
return (Matches("on",value) || Matches("true",value) ||
|
|
Matches("yes",value) || Matches("1",value));
|
|
}
|
|
|
|
bool ProcessOptions::IsFalse(const char *value)
|
|
{
|
|
return (Matches("off",value) || Matches("false",value) ||
|
|
Matches("no",value) || Matches("0",value));
|
|
}
|
|
|
|
bool ProcessOptions::Matches(const char *v1, const char *v2)
|
|
{
|
|
int i = 0;
|
|
int ans = 1;
|
|
|
|
while((ans) && (v1[i] != 0) && (v2[i] != 0))
|
|
{
|
|
ans = ans && (int)(v1[i] == tolower(v2[i]));
|
|
i++;
|
|
}
|
|
|
|
return (ans != 0);
|
|
}
|
|
|
|
int ProcessOptions::POVMSAttr_SetUTF8String(POVMSAttributePtr attr, POVMSType type, const char *s)
|
|
{
|
|
UCS2 *ustr = new UCS2[strlen(s) + 1];
|
|
size_t len = ConvertUTF8ToUCS2(s, ustr);
|
|
int err = POVMSAttr_Set(attr, type, (void *)ustr, (int(len) * 2) + 2);
|
|
|
|
delete[] ustr;
|
|
|
|
return err;
|
|
}
|
|
|
|
int ProcessOptions::POVMSAttr_GetUTF8String(POVMSAttributePtr attr, POVMSType type, char *s, int *maxdatasize)
|
|
{
|
|
int ulen = 0;
|
|
int err = POVMSAttr_Size(attr, &ulen);
|
|
UCS2 *ustr = new UCS2[ulen / 2 + 1];
|
|
|
|
if(err == kNoErr)
|
|
err = POVMSAttr_Get(attr, type, (void *)ustr, &ulen);
|
|
|
|
if(err == kNoErr)
|
|
*maxdatasize = ConvertUCS2ToUTF8(ustr, s);
|
|
else
|
|
*maxdatasize = 0;
|
|
|
|
delete[] ustr;
|
|
|
|
return err;
|
|
}
|
|
|
|
int ProcessOptions::POVMSUtil_SetUTF8String(POVMSObjectPtr object, POVMSType key, const char *s)
|
|
{
|
|
UCS2 *ustr = new UCS2[strlen(s) + 1];
|
|
size_t len = ConvertUTF8ToUCS2(s, ustr);
|
|
int err = POVMSUtil_SetUCS2String(object, key, ustr);
|
|
|
|
delete[] ustr;
|
|
|
|
return err;
|
|
}
|
|
|
|
const unsigned char gUTF8SequenceArray[256] =
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
|
|
};
|
|
|
|
const unsigned int gUTF8Offsets[6] =
|
|
{
|
|
0x00000000UL,
|
|
0x00003080UL,
|
|
0x000E2080UL,
|
|
0x03C82080UL,
|
|
0xFA082080UL,
|
|
0x82082080UL
|
|
};
|
|
|
|
// Note that this is duplicate code, but cleaning up all the string handling is beyond the scope of POV-Ray 3.7 :-( [trf]
|
|
size_t ProcessOptions::ConvertUTF8ToUCS2(const char *text_array, UCS2 *char_array)
|
|
{
|
|
UCS4 chr;
|
|
int i, j, k, seqlen;
|
|
int text_array_size = strlen(text_array);
|
|
|
|
for(i = 0, k = 0; i < text_array_size; k++, i++)
|
|
{
|
|
seqlen = gUTF8SequenceArray[text_array[i]];
|
|
chr = 0;
|
|
for(j = seqlen; j > 0; j--)
|
|
{
|
|
chr += text_array[i];
|
|
chr <<= 6;
|
|
i++;
|
|
}
|
|
|
|
chr += text_array[i];
|
|
chr -= gUTF8Offsets[seqlen];
|
|
|
|
if(chr <= 0x0000FFFFUL)
|
|
char_array[k] = chr;
|
|
else
|
|
char_array[k] = 0x0000FFFDUL;
|
|
}
|
|
|
|
char_array[k] = 0;
|
|
|
|
return k;
|
|
}
|
|
|
|
size_t ProcessOptions::ConvertUCS2ToUTF8(const UCS2 *char_array, char *text_array)
|
|
{
|
|
int i, k, len;
|
|
unsigned int chr;
|
|
|
|
for(len = 0, i = 0; char_array[i] != 0; i++)
|
|
{
|
|
if(char_array[i] < 0x00000080)
|
|
len += 1;
|
|
else if(char_array[i] < 0x00000800)
|
|
len += 2;
|
|
else
|
|
len += 3;
|
|
}
|
|
|
|
for(i = 0, k = 0; (char_array[i] != 0) && (k < len); i++)
|
|
{
|
|
chr = char_array[i];
|
|
|
|
if(chr < 0x00000080)
|
|
{
|
|
text_array[k] = char(chr);
|
|
k++;
|
|
}
|
|
else if (chr < 0x00000800)
|
|
{
|
|
text_array[k] = ((chr >> 6) | 0xC0);
|
|
k++;
|
|
text_array[k] = ((chr | 0x80) & 0xBF);
|
|
k++;
|
|
}
|
|
else
|
|
{
|
|
if(chr >= 0x00010000)
|
|
chr = (unsigned int)0x0000FFFDUL;
|
|
|
|
text_array[k] = ((chr >> 12) | 0xE0);
|
|
k++;
|
|
text_array[k] = (((chr >> 6) | 0x80) & 0xBF);
|
|
k++;
|
|
text_array[k] = ((chr | 0x80) & 0xBF);
|
|
k++;
|
|
}
|
|
}
|
|
|
|
text_array[k] = 0;
|
|
|
|
return size_t(len);
|
|
}
|
|
|
|
int ProcessOptions::ReadSpecialOptionHandler(INI_Parser_Table *, char *, POVMSObjectPtr)
|
|
{
|
|
// do nothing by default
|
|
return kNoErr;
|
|
}
|
|
|
|
int ProcessOptions::ReadSpecialSwitchHandler(Cmd_Parser_Table *, char *, POVMSObjectPtr, bool)
|
|
{
|
|
// do nothing by default
|
|
return kNoErr;
|
|
}
|
|
|
|
int ProcessOptions::WriteSpecialOptionHandler(INI_Parser_Table *, POVMSObjectPtr, OTextStream *)
|
|
{
|
|
// do nothing by default
|
|
return kNoErr;
|
|
}
|
|
|
|
bool ProcessOptions::WriteOptionFilter(INI_Parser_Table *)
|
|
{
|
|
// filter nothing out by default
|
|
return true;
|
|
}
|
|
|
|
bool ProcessOptions::ProcessUnknownSwitch(char *, char *, POVMSObjectPtr)
|
|
{
|
|
// accept no unknown switches by default
|
|
return false;
|
|
}
|
|
|
|
int ProcessOptions::ProcessUnknownString(char *, POVMSObjectPtr)
|
|
{
|
|
// do nothing by default
|
|
return kNoErr;
|
|
}
|
|
|
|
void ProcessOptions::ParseError(const char *format, ...)
|
|
{
|
|
va_list marker;
|
|
char error_buffer[1024];
|
|
|
|
va_start(marker, format);
|
|
vsnprintf(error_buffer, 1023, format, marker);
|
|
va_end(marker);
|
|
|
|
fprintf(stderr, "%s\n", error_buffer);
|
|
}
|
|
|
|
void ProcessOptions::ParseErrorAt(ITextStream *file, const char *format, ...)
|
|
{
|
|
va_list marker;
|
|
char error_buffer[1024];
|
|
|
|
va_start(marker, format);
|
|
vsnprintf(error_buffer, 1023, format, marker);
|
|
va_end(marker);
|
|
|
|
fprintf(stderr, "%s\nFile '%s' at line '%u'", error_buffer, UCS2toASCIIString(file->name()).c_str(), (unsigned int) file->line());
|
|
}
|
|
|
|
void ProcessOptions::WriteError(const char *format, ...)
|
|
{
|
|
va_list marker;
|
|
char error_buffer[1024];
|
|
|
|
va_start(marker, format);
|
|
vsnprintf(error_buffer, 1023, format, marker);
|
|
va_end(marker);
|
|
|
|
fprintf(stderr, "%s\n", error_buffer);
|
|
}
|
|
|
|
int ProcessOptions::Output_INI_Option(INI_Parser_Table *option, POVMSObjectPtr obj, OTextStream *file)
|
|
{
|
|
POVMSFloat floatval;
|
|
POVMSBool b;
|
|
POVMSInt intval;
|
|
int err = 0;
|
|
int l;
|
|
POVMSAttribute item;
|
|
char *bufptr;
|
|
|
|
switch(option->type)
|
|
{
|
|
case kPOVMSType_Int:
|
|
if(POVMSUtil_GetInt(obj, option->key, &intval) == 0)
|
|
file->printf("%s=%d\n", option->keyword, (int)intval);
|
|
break;
|
|
case kPOVMSType_Float:
|
|
if(POVMSUtil_GetFloat(obj, option->key, &floatval) == 0)
|
|
file->printf("%s=%g\n", option->keyword, (float)floatval);
|
|
break;
|
|
case kPOVMSType_Bool:
|
|
if(POVMSUtil_GetBool(obj, option->key, &b) == 0)
|
|
{
|
|
if(b == true)
|
|
file->printf("%s=On\n", option->keyword);
|
|
else
|
|
file->printf("%s=Off\n", option->keyword);
|
|
}
|
|
break;
|
|
case kPOVMSType_CString:
|
|
err = POVMSObject_Get(obj, &item, option->key);
|
|
if(err != 0)
|
|
break;
|
|
// get the file name and path string
|
|
l = 0;
|
|
err = POVMSAttr_Size(&item, &l);
|
|
if(l > 0)
|
|
{
|
|
bufptr = new char[l];
|
|
bufptr[0] = 0;
|
|
if(POVMSAttr_Get(&item, kPOVMSType_CString, bufptr, &l) == 0)
|
|
file->printf("%s=\"%s\"\n", option->keyword, bufptr);
|
|
delete[] bufptr;
|
|
}
|
|
(void)POVMSAttr_Delete(&item);
|
|
break;
|
|
case kPOVMSType_UCS2String:
|
|
err = POVMSObject_Get(obj, &item, option->key);
|
|
if(err != 0)
|
|
break;
|
|
// get the file name and path string
|
|
l = 0;
|
|
err = POVMSAttr_Size(&item, &l);
|
|
if(l > 0)
|
|
{
|
|
bufptr = new char[l * 3];
|
|
bufptr[0] = 0;
|
|
if(POVMSAttr_GetUTF8String(&item, kPOVMSType_UCS2String, bufptr, &l) == 0)
|
|
file->printf("%s=\"%s\"\n", option->keyword, bufptr);
|
|
delete[] bufptr;
|
|
}
|
|
(void)POVMSAttr_Delete(&item);
|
|
break;
|
|
case kPOVMSType_WildCard:
|
|
WriteSpecialOptionHandler(option, obj, file);
|
|
break;
|
|
case 0:
|
|
// deprecated option, skip
|
|
break;
|
|
default:
|
|
WriteError("Ignoring unknown INI option.");
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int ProcessOptions::Parse_INI_Specification(const char *filespec, char *&filename, char *§ionname)
|
|
{
|
|
const char *sectionpos = strchr(filespec, '[');
|
|
|
|
// if there is no section string, this is the whole filename
|
|
if(sectionpos == NULL)
|
|
{
|
|
filename = new char[strlen(filespec) + 1];
|
|
strcpy(filename, filespec);
|
|
}
|
|
// if there is a section string, the filename ends at the section beginning
|
|
else
|
|
{
|
|
const char *sectionend = strchr(filespec, ']');
|
|
|
|
// if there was no section end, this file specification is invalid
|
|
if(sectionend == NULL)
|
|
return kParamErr;
|
|
// if there valid section specification, use it
|
|
else
|
|
{
|
|
// section string starts at sectionpos + 1 and has the length sectionend - sectionpos - 1 + terminating zero
|
|
sectionname = new char[sectionend - sectionpos];
|
|
strncpy(sectionname, sectionpos + 1, sectionend - sectionpos - 1);
|
|
sectionname[sectionend - sectionpos - 1] = 0;
|
|
|
|
// filename string ends at sectionpos and the the length sectionpos - filespec + terminating zero
|
|
filename = new char[sectionpos - filespec + 1];
|
|
strncpy(filename, filespec, sectionpos - filespec);
|
|
filename[sectionpos - filespec] = 0;
|
|
}
|
|
}
|
|
|
|
return kNoErr;
|
|
}
|
|
|
|
int ProcessOptions::Parse_INI_Skip_Space(ITextStream *file, bool countnewlines)
|
|
{
|
|
int linecount = 0;
|
|
|
|
// read until the end of file or until a non-space is found
|
|
while(file->eof() == false)
|
|
{
|
|
int chr = file->getchar();
|
|
|
|
// count newlinegets
|
|
if((chr == 10) && (countnewlines == true))
|
|
{
|
|
int c2 = file->getchar();
|
|
if(c2 != 13)
|
|
file->ungetchar(c2);
|
|
linecount++;
|
|
}
|
|
else if((chr == 13) && (countnewlines == true))
|
|
{
|
|
int c2 = file->getchar();
|
|
if(c2 != 10)
|
|
file->ungetchar(c2);
|
|
linecount++;
|
|
}
|
|
// apart from a newline, spaces and tabs are considered "space"
|
|
else if((chr != ' ') && (chr != '\t'))
|
|
{
|
|
file->ungetchar(chr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return linecount;
|
|
}
|
|
|
|
int ProcessOptions::Parse_INI_Skip_Line(ITextStream *file)
|
|
{
|
|
int linecount = 0;
|
|
|
|
// read until the end of file or until a newline is found
|
|
while(file->eof() == false)
|
|
{
|
|
int chr = file->getchar();
|
|
|
|
// count newlines
|
|
if(chr == 10)
|
|
{
|
|
int c2 = file->getchar();
|
|
if(c2 != 13)
|
|
file->ungetchar(c2);
|
|
linecount++;
|
|
break;
|
|
}
|
|
else if(chr == 13)
|
|
{
|
|
int c2 = file->getchar();
|
|
if(c2 != 10)
|
|
file->ungetchar(c2);
|
|
linecount++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return linecount;
|
|
}
|
|
|
|
int ProcessOptions::Parse_INI_Option(ITextStream *file, POVMSObjectPtr obj)
|
|
{
|
|
struct INI_Parser_Table *table = parse_ini_table;
|
|
char *value = NULL;
|
|
char *key = NULL;
|
|
int chr = 0;
|
|
int err = kNoErr;
|
|
|
|
// read the key string
|
|
key = Parse_INI_String(file);
|
|
if(key == NULL)
|
|
{
|
|
ParseErrorAt(file, "Expected key in INI file, no key was found.");
|
|
return kParseErr;
|
|
}
|
|
|
|
// find the keyword
|
|
while(table->keyword != NULL)
|
|
{
|
|
if(pov_stricmp(table->keyword, key) == 0)
|
|
break;
|
|
table++;
|
|
}
|
|
|
|
// return if no valid keyword has been found
|
|
if(table->keyword == NULL)
|
|
{
|
|
ParseErrorAt(file, "Unknown key '%s' in INI file.", key);
|
|
delete[] key;
|
|
return kParseErr;
|
|
}
|
|
else
|
|
{
|
|
delete[] key;
|
|
key = NULL;
|
|
}
|
|
|
|
// skip any spaces
|
|
(void)Parse_INI_Skip_Space(file, false);
|
|
|
|
// expect the equal sign
|
|
if(file->getchar() != '=')
|
|
return kParseErr;
|
|
|
|
// skip any spaces
|
|
(void)Parse_INI_Skip_Space(file, false);
|
|
|
|
// TODO: whether or not to apply special parsing ought to be encoded into the INI_Parser_Table,
|
|
// but for now it's hard-coded here. special raw parsing is applied to INI options that we don't
|
|
// want to do much processing on (such as removing quotes or guessing if they are filenames).
|
|
// currently shellout commands are in that category since they may contain quoted paths or
|
|
// quoted/escaped options. We only apply this special case here, where we know the input data
|
|
// is a file (as it's also possible to specify INI options on the command-line).
|
|
if (toupper(table->keyword[strlen(table->keyword) - 1]) == 'D' && // the command and return keywords have the same kPOVAttrib_ values; we only want the command
|
|
(table->key == kPOVAttrib_PostFrameCommand || table->key == kPOVAttrib_PreFrameCommand ||
|
|
table->key == kPOVAttrib_PostSceneCommand || table->key == kPOVAttrib_PreSceneCommand ||
|
|
table->key == kPOVAttrib_UserAbortCommand || table->key == kPOVAttrib_FatalErrorCommand))
|
|
{
|
|
value = Parse_Raw_INI_String(file);
|
|
}
|
|
else
|
|
{
|
|
// if it looks like a quoted string, attempt to extract and handle that
|
|
chr = file->getchar();
|
|
if((chr == '\"') || (chr == '\''))
|
|
value = Parse_INI_String(file, chr);
|
|
// if there were no quotes, just read up to the next space or newline
|
|
else
|
|
{
|
|
file->ungetchar(chr);
|
|
value = Parse_INI_String(file, -2, true);
|
|
}
|
|
}
|
|
|
|
if(value == NULL)
|
|
{
|
|
ParseErrorAt(file, "Expected value in INI file, no value was found.");
|
|
return kParseErr;
|
|
}
|
|
|
|
if(table->key == 0)
|
|
ParseErrorAt(file, "INI option '%s' is no longer used and will result in an error in future versions of POV-Ray.", table->keyword);
|
|
else
|
|
err = Process_INI_Option(table, value, obj);
|
|
|
|
delete[] value;
|
|
value = NULL;
|
|
|
|
// skip any spaces
|
|
(void)Parse_INI_Skip_Space(file, false);
|
|
|
|
// if there is a comma, parse more values and append them
|
|
chr = file->getchar();
|
|
if(chr == ',')
|
|
{
|
|
// read more value strings
|
|
while((file->eof() == false) && (err == kNoErr))
|
|
{
|
|
// skip any spaces
|
|
(void)Parse_INI_Skip_Space(file, false);
|
|
|
|
// if the string is quoted, parse it matching quotes
|
|
chr = file->getchar();
|
|
if((chr == '\"') || (chr == '\''))
|
|
value = Parse_INI_String(file, chr);
|
|
// if there were no quotes, just read up to the next space or newline
|
|
else
|
|
{
|
|
file->ungetchar(chr);
|
|
value = Parse_INI_String(file, -2);
|
|
}
|
|
|
|
if(value == NULL)
|
|
{
|
|
ParseErrorAt(file, "Expected value in INI file, no value was found.");
|
|
return kParseErr;
|
|
}
|
|
|
|
err = Process_INI_Option(table, value, obj);
|
|
delete[] value;
|
|
value = NULL;
|
|
|
|
// skip any spaces
|
|
(void)Parse_INI_Skip_Space(file, false);
|
|
|
|
// if there is no other comma, stop parsing values
|
|
chr = file->getchar();
|
|
if(chr != ',')
|
|
{
|
|
file->ungetchar(chr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
file->ungetchar(chr);
|
|
|
|
return err;
|
|
}
|
|
|
|
int ProcessOptions::Parse_INI_Switch(ITextStream *file, int token, POVMSObjectPtr obj)
|
|
{
|
|
struct Cmd_Parser_Table *table = parse_cmd_table;
|
|
char *value = NULL;
|
|
char *key = NULL;
|
|
int err = kNoErr;
|
|
int chr = 0;
|
|
|
|
// read the switch string
|
|
key = Parse_INI_String(file);
|
|
if(key == NULL)
|
|
{
|
|
ParseErrorAt(file, "Expected command-line switch in INI file, no command-line switch was found.");
|
|
err = kParseErr;
|
|
}
|
|
else
|
|
{
|
|
// if there is a quoted string directory following the switch, parse it matching quotes
|
|
chr = file->getchar();
|
|
if((chr == '\"') || (chr == '\''))
|
|
{
|
|
value = Parse_INI_String(file, chr);
|
|
if(value == NULL)
|
|
ParseErrorAt(file, "Expected command-line switch in INI file to be followed by quoted parameter.");
|
|
}
|
|
else
|
|
file->ungetchar(chr);
|
|
|
|
// find the command-line switch
|
|
while(table->command != NULL)
|
|
{
|
|
char *srcptr = key;
|
|
const char *dstptr = table->command;
|
|
|
|
// compared ignoring case until the end of either string has been reached
|
|
while((toupper(*srcptr) == toupper(*dstptr)) && (*srcptr != 0) && (*dstptr != 0))
|
|
{
|
|
srcptr++;
|
|
dstptr++;
|
|
}
|
|
// if the end of the switch string in the table had been reached, see if there are parameters
|
|
// to consider and if there are, expect the source string to be followed by those parameters
|
|
if((*dstptr) == 0)
|
|
{
|
|
// if there was a quoted value string and the switch string is longer, this is an unknown switch
|
|
if((value != NULL) && (*srcptr != 0))
|
|
{
|
|
table = NULL;
|
|
break;
|
|
}
|
|
// if there was a quoted value string and the switch matches, use the value string as parameter
|
|
else if((value != NULL) && (*srcptr == 0))
|
|
srcptr = value;
|
|
|
|
// only if a paremeter is expected allow it, and vice versa
|
|
if(((*srcptr > ' ') && (table->type != kPOVMSType_Null)) || ((*srcptr <= ' ') && (table->type == kPOVMSType_Null)))
|
|
{
|
|
err = Process_Switch(table, srcptr, obj, (token != '-'));
|
|
break;
|
|
}
|
|
}
|
|
table++;
|
|
}
|
|
|
|
// if there was no sucessful match so far, see if it is a system specific switch
|
|
if((table == NULL) || (table->command == NULL))
|
|
{
|
|
if(ProcessUnknownSwitch(key, value, obj) == false)
|
|
{
|
|
if(value != NULL)
|
|
ParseErrorAt(file, "Unknown switch '%s' with value '%s' in INI file.", key, value);
|
|
else
|
|
ParseErrorAt(file, "Unknown switch '%s' in INI file.", key);
|
|
err = kParseErr;
|
|
}
|
|
else
|
|
err = kNoErr;
|
|
}
|
|
}
|
|
|
|
if(key != NULL)
|
|
delete[] key;
|
|
if(value != NULL)
|
|
delete[] value;
|
|
|
|
return err;
|
|
}
|
|
|
|
char *ProcessOptions::Parse_INI_String(ITextStream *file, int endchr, bool smartmode)
|
|
{
|
|
char *str = new char[65536];
|
|
char *pos = str;
|
|
|
|
while((pos - str) < 65535)
|
|
{
|
|
int chr = file->getchar();
|
|
// terminate the string if the end of file has been reached
|
|
if(chr == EOF)
|
|
break;
|
|
// parse to the next space or special token
|
|
else if((endchr == -1) || (endchr == -2))
|
|
{
|
|
if((smartmode == true) && ((chr == ' ') || (chr == '\t')))
|
|
{
|
|
// In "smart mode" the function called below tries to detect if the
|
|
// user is trying to use an unquoted path as value of an INI option.
|
|
// The detection logic is detailed in the function below!
|
|
file->ungetchar(chr);
|
|
if(Parse_INI_String_Smartmode(file) == false)
|
|
break;
|
|
else
|
|
{
|
|
chr = file->getchar();
|
|
endchr = -3; // switch to special mode
|
|
}
|
|
}
|
|
else if(isspace(chr) || (chr == ',') || (chr == ';') || (chr == '#') || (chr == '\"') || (chr == '\'') ||
|
|
((endchr == -1) && ((chr == '[') || (chr == ']') || (chr == '='))))
|
|
{
|
|
file->ungetchar(chr);
|
|
break;
|
|
}
|
|
}
|
|
// this should only be switched on by "smart mode" and parses to either the end of the line or the comment string
|
|
else if(endchr == -3)
|
|
{
|
|
if((chr == ';') || (chr == '#') || (chr == 10) || (chr == 13))
|
|
{
|
|
file->ungetchar(chr);
|
|
break;
|
|
}
|
|
}
|
|
// parse to the next character specified by the caller in endchr
|
|
else if(chr == endchr)
|
|
break;
|
|
|
|
*pos = chr;
|
|
pos++;
|
|
}
|
|
|
|
*pos = 0;
|
|
|
|
return str;
|
|
}
|
|
|
|
// special case of parsing an INI string: we don't want to interpret anything other than
|
|
// a comment character or end-of-line, as the string itself will be passed on to other
|
|
// code that requires it more or less verbatim. an example of this is the shellout commands,
|
|
// which may contain a quoted program name (perhaps because it embeds spaces) followed by
|
|
// unquoted or quoted parameters.
|
|
// NB returned string may have trailing blanks.
|
|
char *ProcessOptions::Parse_Raw_INI_String(ITextStream *file)
|
|
{
|
|
char *str = new char[65536];
|
|
char *pos = str;
|
|
bool inSQ = false;
|
|
bool inDQ = false;
|
|
bool hadEscape = false;
|
|
const char *msg = "Possible unbalanced quotes detected in INI option being parsed in raw mode";
|
|
|
|
str[0] = 0;
|
|
for (int i = 0; i < 65536; i++, *pos = 0)
|
|
{
|
|
int ch = file->getchar();
|
|
if (hadEscape && (ch == '"' || ch == '\'' || ch == '\\'))
|
|
{
|
|
hadEscape = false;
|
|
*pos++ = ch;
|
|
continue;
|
|
}
|
|
hadEscape = false;
|
|
switch (ch)
|
|
{
|
|
case EOF:
|
|
if (inSQ || inDQ)
|
|
ParseErrorAt(file, msg);
|
|
return str;
|
|
|
|
case '\r':
|
|
case '\n':
|
|
file->ungetchar(ch);
|
|
if (inSQ || inDQ)
|
|
ParseErrorAt(file, msg);
|
|
return str;
|
|
|
|
case ';':
|
|
case '#':
|
|
if (!inSQ && !inDQ)
|
|
{
|
|
file->ungetchar(ch);
|
|
return str;
|
|
}
|
|
break;
|
|
|
|
case '\\':
|
|
hadEscape = true;
|
|
break;
|
|
|
|
case '"':
|
|
inDQ = !inDQ;
|
|
break;
|
|
|
|
case '\'':
|
|
inSQ = !inSQ;
|
|
break ;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
*pos++ = ch;
|
|
}
|
|
|
|
if (inSQ || inDQ)
|
|
ParseErrorAt(file, msg);
|
|
return str;
|
|
}
|
|
|
|
bool ProcessOptions::Parse_INI_String_Smartmode(ITextStream *file)
|
|
{
|
|
ITextStream::FilePos backtrackpos = file->tellg();
|
|
bool result = false; // false - end string here, true - continue parsing string
|
|
struct INI_Parser_Table *table = parse_ini_table;
|
|
char *key = NULL;
|
|
|
|
(void)Parse_INI_Skip_Space(file, false);
|
|
|
|
switch(file->getchar())
|
|
{
|
|
// end of file
|
|
case EOF:
|
|
break; // return false, parsing more of the string simply is not possible
|
|
// INI file comment
|
|
case ';':
|
|
case '#':
|
|
break; // return false, this is a comment which terminates the string
|
|
// INI value list separator
|
|
case ',':
|
|
break; // return false, this is a value list of unquoted strings
|
|
// POV-Ray-style INI file with command-line switch
|
|
case '+':
|
|
case '-':
|
|
// POV-Ray-style INI file with system specific command-line switch on some systems (i.e. Windos)
|
|
#if(FILENAME_SEPARATOR != '/')
|
|
case '/':
|
|
#endif
|
|
if(isalpha(file->getchar()))
|
|
break; // return false, this is most likely a command-line
|
|
else
|
|
file->seekg(backtrackpos); // most likely an unquoted string, so allow parsing it as a whole
|
|
// INI file option
|
|
default:
|
|
// read the key string
|
|
key = Parse_INI_String(file);
|
|
if(key != NULL)
|
|
{
|
|
// find the keyword
|
|
while(table->keyword != NULL)
|
|
{
|
|
if(pov_stricmp(table->keyword, key) == 0)
|
|
break;
|
|
table++;
|
|
}
|
|
|
|
// if no valid keyword has been found
|
|
if(table->keyword == NULL)
|
|
{
|
|
result = true; // return true, this is most likely an unquoted path
|
|
ParseErrorAt(file,
|
|
"Most likely detected an unquoted string with spaces in INI file. Assuming string ends at the of the line.\n"
|
|
"Make sure all strings with spaces are properly quoted in the INI file.\n"
|
|
"Use either \" or \' to quote strings. For details, please check the user manual!");
|
|
}
|
|
|
|
delete[] key;
|
|
key = NULL;
|
|
}
|
|
break; // return false, unless the code above did not find a valid keyword
|
|
}
|
|
|
|
file->seekg(backtrackpos);
|
|
|
|
return result;
|
|
}
|
|
|
|
void ProcessOptions::Parse_CL_Skip_Space(const char *&commandline)
|
|
{
|
|
// read until the end of the string or until a non-space is found
|
|
while(*commandline != 0)
|
|
{
|
|
// spaces and tabs are considered "space"
|
|
if((*commandline != ' ') && (*commandline != '\t'))
|
|
break;
|
|
commandline++;
|
|
}
|
|
}
|
|
|
|
int ProcessOptions::Parse_CL_Switch(const char *&commandline, int token, POVMSObjectPtr obj, bool singleswitch)
|
|
{
|
|
struct Cmd_Parser_Table *table = parse_cmd_table;
|
|
char *value = NULL;
|
|
char *key = NULL;
|
|
int err = kNoErr;
|
|
int chr = 0;
|
|
|
|
// read the switch string
|
|
if(singleswitch == false) // see if quotes had been stripped outside POV-Ray
|
|
key = Parse_CL_String(commandline);
|
|
else
|
|
key = Parse_CL_String(commandline, 0);
|
|
if(key == NULL)
|
|
{
|
|
ParseError("Expected command-line switch on command-line, no command-line switch was found.");
|
|
err = kParseErr;
|
|
}
|
|
else
|
|
{
|
|
// if there is a quoted string directly following the switch, parse its matching quotes
|
|
chr = *commandline;
|
|
commandline++;
|
|
if((chr == '\"') || (chr == '\''))
|
|
{
|
|
value = Parse_CL_String(commandline, chr);
|
|
if(value == NULL)
|
|
ParseError("Expected command-line switch on command-line to be followed by quoted parameter.");
|
|
}
|
|
else
|
|
commandline--;
|
|
|
|
// find the command-line switch
|
|
while(table->command != NULL)
|
|
{
|
|
char *srcptr = key;
|
|
const char *dstptr = table->command;
|
|
|
|
// compared ignoring case until the end of either string has been reached
|
|
while((toupper(*srcptr) == toupper(*dstptr)) && (*srcptr != 0) && (*dstptr != 0))
|
|
{
|
|
srcptr++;
|
|
dstptr++;
|
|
}
|
|
// if the end of the switch string in the table had been reached, see if there are parameters
|
|
// to consider and if there are, expect the source string to be followed by those parameters
|
|
if((*dstptr) == 0)
|
|
{
|
|
// if there was a quoted value string and the switch string is longer, this is an unknown switch
|
|
if((value != NULL) && (*srcptr != 0))
|
|
{
|
|
table = NULL;
|
|
break;
|
|
}
|
|
// if there was a quoted value string and the switch matches, use the value string as parameter
|
|
else if((value != NULL) && (*srcptr == 0))
|
|
srcptr = value;
|
|
|
|
// only if a paremeter is expected allow it, and vice versa
|
|
if(((*srcptr > ' ') && (table->type != kPOVMSType_Null)) || ((*srcptr <= ' ') && (table->type == kPOVMSType_Null)))
|
|
{
|
|
err = Process_Switch(table, srcptr, obj, (token != '-'));
|
|
break;
|
|
}
|
|
}
|
|
table++;
|
|
}
|
|
|
|
// if there was no successful match so far, see if it is a system specific switch
|
|
if((table == NULL) || (table->command == NULL))
|
|
{
|
|
if(ProcessUnknownSwitch(key, value, obj) == false)
|
|
{
|
|
if(value != NULL)
|
|
ParseError("Unknown switch '%s' with value '%s' on command-line.", key, value);
|
|
else
|
|
ParseError("Unknown switch '%s' on command-line.", key);
|
|
err = kParseErr;
|
|
}
|
|
else
|
|
err = kNoErr;
|
|
}
|
|
}
|
|
|
|
if(key != NULL)
|
|
delete[] key;
|
|
if(value != NULL)
|
|
delete[] value;
|
|
|
|
return err;
|
|
}
|
|
|
|
int ProcessOptions::Parse_CL_Option(const char *&commandline, POVMSObjectPtr obj, bool singleswitch)
|
|
{
|
|
struct INI_Parser_Table *table = parse_ini_table;
|
|
char *value = NULL;
|
|
char *key = NULL;
|
|
char chr = 0;
|
|
int err = kNoErr;
|
|
|
|
// read the key string
|
|
key = Parse_CL_String(commandline);
|
|
if(key == NULL)
|
|
{
|
|
ParseError("Expected INI file key on command-line, no key was found.");
|
|
return kParseErr;
|
|
}
|
|
|
|
// find the keyword
|
|
while(table->keyword != NULL)
|
|
{
|
|
if(pov_stricmp(table->keyword, key) == 0)
|
|
break;
|
|
table++;
|
|
}
|
|
|
|
// return false if no valid keyword has been found
|
|
if(table->keyword == NULL)
|
|
{
|
|
delete[] key;
|
|
return kParseErr;
|
|
}
|
|
else
|
|
{
|
|
delete[] key;
|
|
key = NULL;
|
|
}
|
|
|
|
// expect the equal sign
|
|
if(*commandline != '=')
|
|
return kParseErr;
|
|
commandline++;
|
|
|
|
// if the string is quoted, parse it matching quotes
|
|
chr = *commandline;
|
|
if((chr == '\"') || (chr == '\''))
|
|
{
|
|
commandline++;
|
|
|
|
value = Parse_CL_String(commandline, chr);
|
|
}
|
|
// if there were no quotes, just read up to the next space or newline
|
|
else if(singleswitch == false) // see if quotes had been stripped outside POV-Ray
|
|
value = Parse_CL_String(commandline, -2);
|
|
else
|
|
value = Parse_CL_String(commandline, 0);
|
|
|
|
if(value == NULL)
|
|
{
|
|
ParseError("Expected value on command-line, no value was found.");
|
|
return kParseErr;
|
|
}
|
|
|
|
err = Process_INI_Option(table, value, obj);
|
|
delete[] value;
|
|
value = NULL;
|
|
|
|
return err;
|
|
}
|
|
|
|
char *ProcessOptions::Parse_CL_String(const char *&commandline, int endchr)
|
|
{
|
|
int maxlen = strlen(commandline) + 1;
|
|
char *str = new char[maxlen];
|
|
char *pos = str;
|
|
|
|
while(*commandline != 0)
|
|
{
|
|
int chr = *commandline;
|
|
|
|
if(endchr <= -1)
|
|
{
|
|
if(isspace(chr) || (chr == ';') || (chr == '#') || (chr == '\"') || (chr == '\''))
|
|
break;
|
|
else if((endchr == -1) && ((chr == '[') || (chr == ']') || (chr == '=')))
|
|
break;
|
|
}
|
|
|
|
commandline++;
|
|
|
|
if(chr == endchr)
|
|
break;
|
|
|
|
*pos = chr;
|
|
pos++;
|
|
}
|
|
|
|
*pos = 0;
|
|
|
|
return str;
|
|
}
|
|
|
|
int ProcessOptions::Process_INI_Option(INI_Parser_Table *option, char *param, POVMSObjectPtr obj)
|
|
{
|
|
double floatval = 0.0;
|
|
int intval = 0;
|
|
int err = kNoErr;
|
|
|
|
switch(option->type)
|
|
{
|
|
case kPOVMSType_Int:
|
|
if(sscanf(param, "%d", &intval) == 1)
|
|
err = POVMSUtil_SetInt(obj, option->key, intval);
|
|
else
|
|
{
|
|
ParseError("Integer parameter expected for option '%s', found '%s'.", option->keyword, param);
|
|
err = kParseErr;
|
|
}
|
|
break;
|
|
case kPOVMSType_Float:
|
|
if(sscanf(param, "%lf", &floatval) == 1)
|
|
err = POVMSUtil_SetFloat(obj, option->key, floatval);
|
|
else
|
|
{
|
|
ParseError("Floating-point parameter expected for option '%s', found '%s'.", option->keyword, param);
|
|
err = kParseErr;
|
|
}
|
|
break;
|
|
case kPOVMSType_Bool:
|
|
err = POVMSUtil_SetBool(obj, option->key, IsTrue(param));
|
|
break;
|
|
case kPOVMSType_CString:
|
|
err = POVMSUtil_SetString(obj, option->key, param);
|
|
break;
|
|
case kPOVMSType_UCS2String:
|
|
err = POVMSUtil_SetUTF8String(obj, option->key, param);
|
|
break;
|
|
case kPOVMSType_WildCard:
|
|
err = ReadSpecialOptionHandler(option, param, obj);
|
|
break;
|
|
default:
|
|
err = kParseErr;
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int ProcessOptions::Process_Switch(Cmd_Parser_Table *option, char *param, POVMSObjectPtr obj, bool is_on)
|
|
{
|
|
double floatval = 0.0;
|
|
int intval = 0;
|
|
int err = 0;
|
|
|
|
if(option->is_switch != kPOVMSType_Null)
|
|
{
|
|
err = POVMSUtil_SetBool(obj, option->is_switch, is_on);
|
|
if(err != kNoErr)
|
|
return err;
|
|
}
|
|
|
|
switch(option->type)
|
|
{
|
|
case kPOVMSType_Int:
|
|
if(sscanf(param, "%d", &intval) == 1)
|
|
err = POVMSUtil_SetInt(obj, option->key, intval);
|
|
else
|
|
{
|
|
ParseError("Integer parameter expected for switch '%s', found '%s'.", option->command, param);
|
|
err = kParseErr;
|
|
}
|
|
break;
|
|
case kPOVMSType_Float:
|
|
if(sscanf(param, "%lf", &floatval) == 1)
|
|
err = POVMSUtil_SetFloat(obj, option->key, floatval);
|
|
else
|
|
{
|
|
ParseError("Floating-point parameter expected for switch '%s', found '%s'.", option->command, param);
|
|
err = kParseErr;
|
|
}
|
|
break;
|
|
case kPOVMSType_Bool:
|
|
err = POVMSUtil_SetBool(obj, option->key, IsTrue(param));
|
|
break;
|
|
case kPOVMSType_CString:
|
|
err = POVMSUtil_SetString(obj, option->key, param);
|
|
break;
|
|
case kPOVMSType_UCS2String:
|
|
err = POVMSUtil_SetUTF8String(obj, option->key, param);
|
|
break;
|
|
case kPOVMSType_WildCard:
|
|
err = ReadSpecialSwitchHandler(option, param, obj, is_on);
|
|
break;
|
|
case kPOVMSType_Null:
|
|
break;
|
|
default:
|
|
err = kParseErr;
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
}
|