// Copyright (C) 2005 David Sugar, Tycho Softworks.
//
// This program is free software; you can 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 of the License, or
// (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction. Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License. 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 under the name GNU
// ccScript. If you copy code from other releases into a copy of GNU
// ccScript, 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 GNU ccScript, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//
#include "script3.h"
#include <cstdio>
#ifdef WIN32
#define EXT_PROP ".pro"
#define EXT_TEMP ".tmp"
#define PRE_PROP "\\Script Properties\\"
#define PRE_MAKE "\\Script Properties"
#else
#define EXT_PROP ".init"
#define EXT_TEMP ".temp"
#define PRE_PROP "/.properties/"
#define PRE_MAKE "/.properties"
#endif
#define MAX_LIST 256
namespace ccscript3Extension {
using namespace std;
using namespace ost;
class LoadThread : public ScriptThread
{
private:
FILE *fp;
Symbol *list[MAX_LIST];
char path[128];
char var[128];
void run(void);
public:
LoadThread(ScriptInterp *interp);
~LoadThread();
};
class SaveThread : public ScriptThread
{
private:
FILE *fp, *fr;
Symbol *list[MAX_LIST];
char path[128];
char temp[128];
char var[128];
const char *id;
void run(void);
public:
SaveThread(ScriptInterp *interp, const char *uid);
~SaveThread();
};
class PropertyChecks : public ScriptChecks
{
public:
const char *chkIO(Line *line, ScriptImage *img);
const char *chkVar(Line *line, ScriptImage *img);
};
class PropertyMethods : public ScriptMethods
{
public:
bool scrLoad(void);
bool scrSave(void);
bool scrVar(void);
};
class PropertyBinder : public ScriptBinder
{
public:
PropertyBinder(Script::Define *run);
};
static Script::Define runtime[] = {
{"load", false, (Script::Method)&PropertyMethods::scrLoad,
(Script::Check)&PropertyChecks::chkIO},
{"save", false, (Script::Method)&PropertyMethods::scrSave,
(Script::Check)&PropertyChecks::chkIO},
{"prop", true, (Script::Method)&PropertyMethods::scrVar,
(Script::Check)&PropertyChecks::chkVar},
{"property", true, (Script::Method)&PropertyMethods::scrVar,
(Script::Check)&PropertyChecks::chkVar},
{NULL, false, NULL, NULL}};
PropertyBinder::PropertyBinder(Script::Define *run) :
ScriptBinder(run)
{
char path[128];
if(*Script::var_prefix != '.')
{
snprintf(path, sizeof(path), "%s" PRE_MAKE,
Script::var_prefix);
Dir::create(path);
}
}
static PropertyBinder bindProperty(runtime);
LoadThread::LoadThread(ScriptInterp *interp) :
ScriptThread(interp, 0)
{
fp = NULL;
}
SaveThread::SaveThread(ScriptInterp *interp, const char *uid) :
ScriptThread(interp, 0)
{
fp = NULL;
fr = NULL;
id = uid;
}
LoadThread::~LoadThread()
{
terminate();
if(fp)
::fclose(fp);
}
SaveThread::~SaveThread()
{
terminate();
if(fp)
::fclose(fp);
if(fr)
::fclose(fr);
}
void SaveThread::run(void)
{
char prof[128];
char buf[128];
const char *grp = interp->getMember();
const char *cp;
char *ep;
Symbol *sym;
unsigned idx = 0, count;
char base[64];
Name *scr = interp->getName();
if(!grp)
{
setString(base, sizeof(base), scr->name);
ep = strchr(base, ':');
if(ep)
*ep = 0;
grp = base;
}
#ifdef WIN32
if(*Script::var_prefix == '.')
{
snprintf(temp, sizeof(temp), "%s%s" EXT_TEMP, grp, id);
snprintf(path, sizeof(path), "%s%s" EXT_PROP, grp, id);
}
else
{
snprintf(temp, sizeof(temp), "%s" PRE_PROP "%s%s" EXT_TEMP,
Script::var_prefix, grp, id);
snprintf(path, sizeof(path), "%s" PRE_PROP "%s%s" EXT_PROP,
Script::var_prefix, grp, id);
}
#else
if(*Script::var_prefix == '.')
{
snprintf(path, sizeof(path), "%s.%s", grp, id);
snprintf(temp, sizeof(temp), ".%s.%s", grp, id);
}
else
{
snprintf(temp, sizeof(temp), "%s" PRE_PROP ".%s.%s",
Script::var_prefix, grp, id);
snprintf(path, sizeof(path), "%s" PRE_PROP "%s.%s",
Script::var_prefix, grp, id);
}
#endif
snprintf(prof, sizeof(prof), "%s/%s" EXT_PROP, Script::etc_prefix, grp);
remove(temp);
fp = ::fopen(temp, "w");
if(!fp)
exit("save-failed");
fr = ::fopen(path, "r");
if(!fr)
fr = ::fopen(prof, "r");
while(fr)
{
if(!fgets(buf, sizeof(buf), fr) || feof(fr))
break;
cp = buf;
while(isspace(*cp))
++cp;
if(!isalpha(*cp))
continue;
ep = strrchr(buf, '\r');
if(!ep)
ep = strrchr(buf, '\n');
if(ep)
*ep = 0;
ep = (char *)cp;
while(isalnum(*ep))
++ep;
*(ep++) = 0;
while(isspace(*ep) || *ep == '=')
++ep;
snprintf(var, sizeof(var), "%s.%s", grp, cp);
lock();
sym = interp->mapSymbol(var, 0);
if(sym && sym->type == symMODIFIED)
{
fprintf(fp, "%s = %s\n", cp, sym->data);
sym->type = symORIGINAL;
}
else
fprintf(fp, "%s = %s\n", cp, ep);
release();
Thread::yield();
}
lock();
count = interp->gathertype(list, MAX_LIST - 1, grp, symMODIFIED);
release();
while(idx < count)
{
sym = list[idx++];
sym->type = symORIGINAL;
cp = strrchr(sym->id, '.');
if(!cp)
continue;
fprintf(fp, "%s = %s\n", ++cp, sym->data);
Thread::yield();
}
::fflush(fp);
rename(temp, path);
exit(NULL);
}
void LoadThread::run(void)
{
const char *id;
const char *grp = interp->getMember();
char *cp;
char *ep;
Symbol *sym;
char base[64];
Name *scr = interp->getName();
unsigned count, idx = 0;
if(!grp)
{
setString(base, sizeof(base), scr->name);
ep = strchr(base, ':');
if(ep)
*ep = 0;
grp = base;
}
lock();
id = interp->getKeyword("id");
idx = 0;
count = interp->gathertype(list, MAX_LIST - 1, grp, symMODIFIED);
while(idx < count)
{
sym = list[idx++];
sym->data[0] = 0;
sym->type = symORIGINAL;
}
idx = 0;
count = interp->gathertype(list, MAX_LIST - 1, grp, symORIGINAL);
while(idx < count)
{
sym = list[idx++];
sym->data[0] = 0;
}
release();
if(id && *id)
{
#ifdef WIN32
if(*Script::var_prefix == '.')
snprintf(path, sizeof(path), "%s%s" EXT_PROP, grp, id);
else
snprintf(path, sizeof(path), "%s" PRE_PROP "%s%s" EXT_PROP,
Script::var_prefix, grp, id);
#else
if(*Script::var_prefix == '.')
snprintf(path, sizeof(path), "%s.%s", grp, id);
else
snprintf(path, sizeof(path), "%s" PRE_PROP "%s.%s",
Script::var_prefix, grp, id);
#endif
fp = ::fopen(path, "r");
if(fp)
goto load;
}
snprintf(path, sizeof(path), "%s/%s" EXT_PROP, Script::etc_prefix, grp);
fp = ::fopen(path, "r");
if(!fp)
exit("load-failed");
load:
for(;;)
{
if(!fgets(path, sizeof(path), fp) || feof(fp))
break;
cp = path;
while(isspace(*cp))
++cp;
if(!isalpha(*cp))
continue;
ep = strrchr(path, '\r');
if(!ep)
ep = strrchr(path, '\n');
if(ep)
*ep = 0;
ep = cp;
while(isalnum(*ep))
++ep;
*(ep++) = 0;
while(isspace(*ep) || *ep == '=')
++ep;
snprintf(var, sizeof(var), "%s.%s", grp, cp);
lock();
sym = interp->mapSymbol(var, 0);
if(!sym)
goto unlock;
if(sym->type != symORIGINAL && sym->type != symMODIFIED)
goto unlock;
sym->type = symORIGINAL;
snprintf(sym->data, sym->size + 1, ep);
unlock:
release();
Thread::yield();
}
exit(NULL);
}
bool PropertyMethods::scrVar(void)
{
char base[64];
char var[128];
Symbol *sym;
const char *cp;
const char *prefix = getMember();
char *ep;
unsigned len;
Name *scr = getName();
if(!prefix)
{
setString(base, sizeof(base), scr->name);
ep = strchr(base, ':');
if(ep)
*ep = 0;
prefix = base;
}
while(NULL != (cp = getOption()))
{
if(strchr(cp, '.') || *cp == '%' || *cp == '&')
setString(var, sizeof(var), cp);
else
snprintf(var, sizeof(var), "%s.%s", prefix, cp);
ep = strrchr(var, ':');
if(ep)
{
*(ep++) = 0;
len = atoi(ep);
}
else
len = symsize;
sym = mapSymbol(var, len);
if(!sym)
continue;
if(sym->type != symINITIAL)
continue;
sym->type = symORIGINAL;
}
advance();
return true;
}
bool PropertyMethods::scrLoad(void)
{
release();
new LoadThread(dynamic_cast<ScriptInterp*>(this));
return false;
}
bool PropertyMethods::scrSave(void)
{
const char *id = getKeyword("id");
if(!id || !*id)
{
error("save-missing-id");
return true;
}
release();
new SaveThread(dynamic_cast<ScriptInterp*>(this), id);
return false;
}
const char *PropertyChecks::chkVar(Line *line, ScriptImage *img)
{
unsigned idx = 0;
const char *cp;
if(hasKeywords(line))
return "property uses no keywords";
if(!line->argc)
return "property requires arguments";
while(NULL != (cp = getOption(line, &idx)))
{
if(*cp == '&' || *cp == '%')
goto chksize;
if(!isalpha(*cp))
return "property must be field name";
if(strchr(cp, '.'))
return "field must not be compound";
chksize:
cp = strrchr(cp, ':');
if(!cp)
continue;
++cp;
if(!isdigit(*cp))
return "field size must be number";
}
return NULL;
}
const char *PropertyChecks::chkIO(Line *line, ScriptImage *img)
{
unsigned idx = 0;
// if(!getMember(line))
// return "property requires .group";
if(!useKeywords(line, "=id"))
return "invalid keyword used";
if(getOption(line, &idx))
return "property io uses no arguments";
return NULL;
}
};
syntax highlighted by Code2HTML, v. 0.9.1