// 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 <cc++/slog.h>
#include <cstdio>
namespace ccscript3Extension {
using namespace std;
using namespace ost;
class CSVArrayThread : public ScriptThread
{
private:
Symbol *sym;
char csvbuf[1024];
#ifdef WIN32
HANDLE fh;
#else
int fd;
#endif
void run(void);
public:
CSVArrayThread(ScriptInterp *interp, Symbol *s);
~CSVArrayThread();
};
class CSVThread : public ScriptThread
{
private:
char csvbuf[1024];
#ifdef WIN32
HANDLE fh;
#else
int fd;
#endif
void run(void);
public:
CSVThread(ScriptInterp *interp);
~CSVThread();
};
class CSVChecks : public ScriptChecks
{
public:
const char *chkCSV(Line *line, ScriptImage *img);
const char *chkCSVArray(Line *line, ScriptImage *img);
const char *chkCSVExpand(Line *line, ScriptImage *img);
};
class CSVMethods : public ScriptMethods
{
public:
bool scrCSV(void);
bool scrCSVArray(void);
bool scrCSVExpand(void);
};
CSVThread::CSVThread(ScriptInterp *interp) :
ScriptThread(interp, 0)
{
#ifdef WIN32
fh = INVALID_HANDLE_VALUE;
#else
fd = -1;
#endif
}
CSVArrayThread::CSVArrayThread(ScriptInterp *interp, Symbol *s) :
ScriptThread(interp, 0)
{
#ifdef WIN32
fh = INVALID_HANDLE_VALUE;
#else
fd = -1;
#endif
sym = s;
}
CSVThread::~CSVThread()
{
terminate();
#ifdef WIN32
if(fh != INVALID_HANDLE_VALUE)
CloseHandle(fh);
#else
if(fd > -1)
::close(fd);
#endif
}
CSVArrayThread::~CSVArrayThread()
{
terminate();
#ifdef WIN32
if(fh != INVALID_HANDLE_VALUE)
CloseHandle(fh);
#else
if(fd > -1)
::close(fd);
#endif
}
static Script::Define runtime[] = {
{"csv", false, (Script::Method)&CSVMethods::scrCSV,
(Script::Check)&CSVChecks::chkCSV},
{"csvarray", false, (Script::Method)&CSVMethods::scrCSVArray,
(Script::Check)&CSVChecks::chkCSVArray},
{"csvexpand", false, (Script::Method)&CSVMethods::scrCSVExpand,
(Script::Check)&CSVChecks::chkCSVExpand},
{NULL, false, NULL, NULL}};
static ScriptBinder bindCSV(runtime);
void CSVArrayThread::run(void)
{
unsigned len = 0;
const char *v;
char path[128];
unsigned idx = 0;
Array *a = (Array *)&sym->data;
for(;;)
{
v = NULL;
switch(sym->type)
{
case symARRAY:
if(idx < a->count && idx < a->tail)
v = sym->data + sizeof(Array) + idx * (a->rec + 1);
++idx;
break;
case symSTACK:
if(a->tail != a->head)
{
idx = a->tail;
if(a->tail == 0)
a->tail = a->count - 1;
else
--a->tail;
v = sym->data + sizeof(Array) + idx * (a->rec + 1);
}
break;
case symFIFO:
if(a->head != a->tail)
{
v = sym->data + a->head * (a->rec + 1) + sizeof(Array);
if(++a->head >= a->count)
a->head = 0;
}
default:
break;
}
if(!v)
break;
if(len)
snprintf(csvbuf + len, sizeof(csvbuf) - len - 3,
",\'%s\'", v);
else
snprintf(csvbuf, sizeof(csvbuf) - 3, "\'%s\'", v);
len = (unsigned)strlen(csvbuf);
}
if(!len)
exit("csv-empty");
csvbuf[len++] = '\r';
csvbuf[len++] = '\n';
snprintf(path, sizeof(path), "%s/%s.csv",
Script::log_prefix, interp->getMember());
#ifdef WIN32
DWORD wc;
fh = CreateFile(path, GENERIC_WRITE, 0,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(fh == INVALID_HANDLE_VALUE)
exit("csv-failed");
SetFilePointer(fh, 0, NULL, FILE_END);
WriteFile(fh, csvbuf, len, &wc, NULL);
#else
fd = ::open(path, O_APPEND | O_CREAT | O_WRONLY, 0660);
if(fd < 0)
exit("csv-failed");
if(::write(fd, csvbuf, len) < (int)len)
exit("csv-failed");
#endif
exit(NULL);
}
void CSVThread::run(void)
{
unsigned len = 0;
const char *v;
char path[128];
lock();
while(NULL != (v = interp->getValue(NULL)))
{
if(len)
snprintf(csvbuf + len, sizeof(csvbuf) - len - 3,
",\'%s\'", v);
else
snprintf(csvbuf, sizeof(csvbuf) - 3, "\'%s\'", v);
len = (unsigned)strlen(csvbuf);
release();
Thread::yield();
lock();
}
release();
if(!len)
exit("csv-empty");
csvbuf[len++] = '\r';
csvbuf[len++] = '\n';
snprintf(path, sizeof(path), "%s/%s.csv", Script::log_prefix, interp->getMember());
#ifdef WIN32
DWORD wc;
fh = CreateFile(path, GENERIC_WRITE, 0,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(fh == INVALID_HANDLE_VALUE)
exit("csv-failed");
SetFilePointer(fh, 0, NULL, FILE_END);
WriteFile(fh, csvbuf, len, &wc, NULL);
#else
fd = ::open(path, O_APPEND | O_CREAT | O_WRONLY, 0660);
if(fd < 0)
exit("csv-failed");
if(::write(fd, csvbuf, len) < (int)len)
exit("csv-failed");
#endif
exit(NULL);
}
bool CSVMethods::scrCSV(void)
{
release();
new CSVThread(dynamic_cast<ScriptInterp*>(this));
return false;
}
bool CSVMethods::scrCSVExpand(void)
{
const char *cp = getOption();
Symbol *sym = mapSymbol(cp, 0);
Symbol *dest;
unsigned size = symsize;
unsigned offset = 0;
unsigned len = 0, idx = 0;
Array *a;
cp = getKeyword("size");
if(cp)
size = atoi(cp);
cp = getKeyword("offset");
if(cp)
offset = atoi(cp);
if(!sym)
{
error("no-array");
return true;
}
switch(sym->type)
{
case symARRAY:
case symFIFO:
case symSTACK:
break;
default:
error("invalid-array");
return true;
}
cp = getOption();
dest = mapSymbol(cp, size);
if(!dest)
{
error("destination-missing");
return true;
}
if(dest->type != symINITIAL && dest->type != symNORMAL)
{
error("destination-invalid");
return true;
}
dest->type = symNORMAL;
dest->data[0] = 0;
a = (Array *)&sym->data;
for(;;)
{
cp = NULL;
switch(sym->type)
{
case symARRAY:
if(idx < a->count && idx < a->tail)
cp = sym->data + sizeof(Array) + idx * (a->rec + 1);
++idx;
break;
case symSTACK:
if(a->tail != a->head)
{
idx = a->tail;
if(a->tail == 0)
a->tail = a->count - 1;
else
--a->tail;
cp = sym->data + sizeof(Array) + idx * (a->rec + 1);
}
break;
case symFIFO:
if(a->head != a->tail)
{
cp = sym->data + a->head * (a->rec + 1) + sizeof(Array);
if(++a->head >= a->count)
a->head = 0;
}
default:
break;
}
if(!cp)
break;
if(offset)
{
--offset;
continue;
}
if(len)
snprintf(dest->data + len, dest->size - len,
",\'%s\'", cp);
else
snprintf(dest->data, dest->size,
"\'%s\'", cp);
len = (unsigned)strlen(dest->data);
}
advance();
return true;
}
bool CSVMethods::scrCSVArray(void)
{
const char *cp = getOption();
Symbol *sym = mapSymbol(cp, 0);
if(!sym)
{
error("no-array");
return true;
}
switch(sym->type)
{
case symARRAY:
case symFIFO:
case symSTACK:
break;
default:
error("invalid-array");
return true;
}
release();
new CSVArrayThread(dynamic_cast<ScriptInterp *>(this), sym);
return false;
}
const char *CSVChecks::chkCSV(Line *line, ScriptImage *img)
{
if(!getMember(line))
return "csv requires .filename";
if(hasKeywords(line))
return "csv uses no keywords";
if(!line->argc)
return "csv requires arguments";
return NULL;
}
const char *CSVChecks::chkCSVExpand(Line *line, ScriptImage *img)
{
const char *cp;
unsigned idx = 0;
if(getMember(line))
return "csvexpand has no members";
if(!useKeywords(line, "=size=offset"))
return "invalid keyword for csvexpand";
cp = getOption(line, &idx);
if(!cp)
return "csvexpand requires two arguments";
if(*cp != '%' && *cp != '&')
return "csvexpand requires variable argument";
cp = getOption(line, &idx);
if(!cp)
return "csvexpand requires destination variable";
if(*cp != '%' && *cp != '&')
return "csvexpand requires variable destination";
cp = getOption(line, &idx);
if(cp)
return "too many arguments for csvexpand";
return NULL;
}
const char *CSVChecks::chkCSVArray(Line *line, ScriptImage *img)
{
const char *cp;
if(!getMember(line))
return "csvarray requires .filename";
if(hasKeywords(line))
return "csvarray uses no keywords";
if(line->argc != 1)
return "csvarray requires one arguments";
cp = line->args[0];
if(*cp != '%' && *cp != '&')
return "csvarray requires variable argument";
return NULL;
}
};
syntax highlighted by Code2HTML, v. 0.9.1