// 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 #include 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(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(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; } };