/* * ScriptEdit.cpp * * Copyright (C) 2003 J. "MUFTI" Scheurich * * 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 (see the file "COPYING" for details); if * not, write to the Free Software Foundation, Inc., 675 Mass Ave, * Cambridge, MA 02139, USA. */ #include "ScriptEdit.h" #include #include #include #include #include #include #include #include "SFMFTypes.h" #include "Field.h" #include "EventIn.h" #include "EventOut.h" #include "ExposedField.h" #include "Element.h" #include "DuneApp.h" ScriptEdit::ScriptEdit(NodeScript *node, SWND wnd, EditorReadyCallback editorReadyCallback, void *data) { _scriptNode = node; _timer = NULL; _wnd = wnd; _editorReadyCallback = editorReadyCallback; _data = data; _urlData = NULL; } ScriptEdit::~ScriptEdit() { delete _urlData; } bool ScriptEdit::write2file(int f, const char* string) { if (mywritestr(f, string) < 0) { char msg[1024]; mysnprintf(msg,1023,"%s: %s", _editorFile, strerror(errno)); swMessageBox(TheApp->mainWnd(), msg, "Save", SW_MB_OK, SW_MB_WARNING); return false; } return true; } // strstr like function, that do not care about whitespace characters //static bool bool strwhitespacestr(const char* string, const char* what) { char *whatPtr = strdup(what); int whatLen = strlen(whatPtr); // split a whitespace seperated whatPtr into zero seperated substrings for (int i = 0; i < whatLen; i++) if (isspace(whatPtr[i])) whatPtr[i] = (char)0; const char *stringPtr = string; int stringLen = strlen(stringPtr); char *stringEnd = (char *)stringPtr + stringLen; while (stringPtr < stringEnd) { int whatStart = 0; // skip first whitespaces of "what" while ((whatStart < whatLen) && (whatPtr[whatStart]==0)) whatStart++; // search for first "what" substring in rest of string if (stringPtr = strstr(stringPtr, whatPtr + whatStart)) { int stringStart = 0; stringPtr += strlen(whatPtr+whatStart); whatStart += strlen(whatPtr+whatStart); while (whatStart <= whatLen) { // find next "what" substring while ((whatStart < whatLen) && (whatPtr[whatStart]==0)) whatStart++; // "what" string complete ? if (whatStart >= whatLen) { free(whatPtr); return true; } // skip whitespaces in string while (isspace(stringPtr[stringStart])) stringStart++; // compare next substring if (stringncmp(stringPtr+stringStart, whatPtr+whatStart)==0) { stringStart += strlen(whatPtr+whatStart); whatStart += strlen(whatPtr+whatStart); } else break; } } else { break; } } free(whatPtr); return false; } // add javascript scheme if necessary and write to file to be edited bool ScriptEdit::WriteSFStringUrl(int f, char* string) { bool isJavascript = false; Proto* proto = _scriptNode->getProto(); if (proto == NULL) return false; if (!write2file(f, "\"")) return false; if (strlen(string) == 0) { if (!write2file(f, "javascript:\n\n")) return false; if (!write2file(f, "// insert program code only into functions\n\n")) return false; isJavascript = true; } if ((stringncmp(string, "javascript:") == 0)) { if (!write2file(f, "javascript:\n\n")) return false; isJavascript = true; string += strlen("javascript:"); if (string[0] == '\n') string++; if (string[0] == '\n') string++; } bool addComment = true; bool addInitialize = TheApp->GetEcmaScriptAddInitialise(); bool addShutdown = TheApp->GetEcmaScriptAddShutdown(); bool addEventsProcessed = TheApp->GetEcmaScriptAddEventsProcessed(); MyString javascript = ""; InterfaceArray *interfaceData = _scriptNode->getInterfaceData(); for (int j = 0; j < interfaceData->size(); j++) { int ind = interfaceData->get(j)->_elementIndex; MyString text = ""; MyString cmptext = ""; MyString extratext = ""; switch (interfaceData->get(j)->_elementEnum) { case EL_FIELD_DEF: { MyString name = proto->getField(ind)->getName(); int type = proto->getField(ind)->getType(); text += " // field "; text += typeEnumToString(type); text += " "; text += name; text += " //"; cmptext = text; extratext += "\n"; if (addComment) { extratext += typeDefaultValue(type)-> getEcmaScriptComment(name, EL_FIELD_DEF); extratext += "\n"; } } break; case EL_EVENT_OUT: { MyString name = proto->getEventOut(ind)->getName(); int type = proto->getEventOut(ind)->getType(); text += " // eventOut "; text += typeEnumToString(type); text += " "; text += name; text += " //"; cmptext = text; extratext += "\n"; if (addComment) { extratext += typeDefaultValue(type)-> getEcmaScriptComment(name, EL_EVENT_OUT); extratext += "\n"; } } break; case EL_EVENT_IN: { MyString name = proto->getEventIn(ind)->getName(); int type = proto->getEventIn(ind)->getType(); text += " function "; text += name; cmptext = ""; cmptext += text; text += "("; cmptext += " ("; extratext = "value)\n {"; extratext += "\n // value "; extratext += typeEnumToString(type); extratext += "\n"; if (addComment) { extratext += typeDefaultValue(type)-> getEcmaScriptComment("value", EL_EVENT_IN); extratext += "\n"; } extratext += " }\n\n"; } break; } // if not already in, attach to javascript code if (strwhitespacestr(string, cmptext) == false) { javascript += text; javascript += extratext; } } if (addInitialize) { MyString text = " function initialize("; MyString cmptext = " function initialize ("; MyString extratext = ")\n {\n \n }\n\n"; // if not already in, attach to javascript code if (strwhitespacestr(string, cmptext) == false) { javascript += text; javascript += extratext; } } if (addEventsProcessed) { MyString text = " function eventsProcessed("; MyString cmptext = " function eventsProcessed ("; MyString extratext = ")\n {\n \n }\n\n"; // if not already in, attach to javascript code if (strwhitespacestr(string, cmptext) == false) { javascript += text; javascript += extratext; } } if (addShutdown) { MyString text = " function shutdown("; MyString cmptext = " function shutdown("; MyString extratext = ")\n {\n \n }\n\n"; // if not already in, attach to javascript code if (strwhitespacestr(string, cmptext) == false) { javascript += text; javascript += extratext; } } if (isJavascript) if (!write2file(f, javascript)) return false; if (!write2file(f, string)) return false; if (!write2file(f, "\"")) return false; return true; } char* ScriptEdit::ecmaScriptEdit(void) { int texteditUseExtensionTxt; swTexteditGetSettingsUseExtensionTxt(TheApp->GetTextedit(), &texteditUseExtensionTxt); if (texteditUseExtensionTxt) swGetTempFile(_editorFile,".dune_ecmascript", ".txt", 1024); else swGetTempFile(_editorFile,".dune_ecmascript", ".js", 1024); int f = open(_editorFile, O_WRONLY | O_CREAT,00666); if (f == -1) { if (texteditUseExtensionTxt) swGetTempPath(_editorFile,".dune_ecmascript",".txt",1024); else swGetTempPath(_editorFile,".dune_ecmascript",".js",1024); f = open(_editorFile, O_WRONLY | O_CREAT,00666); if (f == -1) { char msg[1024]; mysnprintf(msg,1023,"unable to save files to edit %s: %s\n %s", _editorFile, strerror(errno), "save to a writeable directory first"); swMessageBox(TheApp->mainWnd(), msg, "temporary save", SW_MB_OK, SW_MB_WARNING); return NULL; } } TheApp->AddToFilesToDelete(_editorFile); bool writeError = false; MFString *url = (MFString *) _scriptNode->getField(SCRIPT_URL_FIELD); bool hasUrl = true; if (url == NULL) hasUrl = false; if (url->getSize() == 0) hasUrl = false; if (hasUrl) for (int i=0; i < url->getSize() ; i++) { char* string= (char*) ((const char*) url->getValue(i)); if (!WriteSFStringUrl(f, string)) writeError = true; if (i < url->getSize()-1) if (!write2file(f, ",\n")) writeError = true; } else if (!WriteSFStringUrl(f, "")) writeError = true; if (writeError) { char msg[1024]; mysnprintf(msg,1023,"%s: %s", _editorFile, strerror(errno)); swMessageBox(TheApp->mainWnd(), msg, "dune: edit Script", SW_MB_OK, SW_MB_WARNING); return NULL; } #ifndef _WIN32 ftruncate(f,lseek(f,0,SEEK_CUR)); #else _chsize(f, tell(f)); #endif close(f); if (ecmaScriptStartEditor()) return NULL; else return _editorFile; } static int EcmaScriptEditTimerCallback(void *data) { ScriptEdit *scriptEdit = (ScriptEdit *) data; scriptEdit->OnTimer(); return 0; } bool ScriptEdit::ecmaScriptStartEditor(void) { const char *texteditCommand; const char *texteditLinenumberOption; int texteditUseExtensionTxt; int texteditAllowPopup; swTexteditGetSettings(TheApp->GetTextedit(), &texteditCommand, &texteditLinenumberOption, &texteditUseExtensionTxt, &texteditAllowPopup); if (texteditAllowPopup) { MyString command = strdup(texteditCommand); command += " "; command += _editorFile; _timer = swSetTimer(_wnd, 500, EcmaScriptEditTimerCallback, this); if (swCreateCheckableProcess((const char*)command) !=0) { char msg[1024]; #ifdef WIN32 mysnprintf(msg,1023,"%s: %s", _editorFile, strerror(GetLastError())); #else mysnprintf(msg,1023,"%s: %s", _editorFile, strerror(errno)); #endif swMessageBox(TheApp->mainWnd(), msg, "dune: edit Script", SW_MB_OK, SW_MB_WARNING); } return true; } else return false; } void ScriptEdit::OnTimer(void) { if (_timer) { swKillTimer(_timer); _timer = NULL; if (swCheckRunningProcess()) _timer = swSetTimer(_wnd, 500, EcmaScriptEditTimerCallback, this); else { ecmaScriptReadEditorfile(); _editorReadyCallback(_data); } } } void ScriptEdit::ecmaScriptReadEditorfile(char *fileName) { char *file = fileName; if (file == NULL) file = _editorFile; int f = open(file, O_RDONLY, 00666); if (f == -1) { char msg[1024]; mysnprintf(msg,1023,"can not read Script data from file %s: %s", file, strerror(errno)); swMessageBox(TheApp->mainWnd(), msg, "dune: Edit Script", SW_MB_OK, SW_MB_WARNING); return; } lseek(f, 0, SEEK_SET); _urlDataLength = lseek(f, 0, SEEK_END); lseek(f,0,SEEK_SET); _urlData = new char[_urlDataLength]; int offset = 0; do { int bytes = read(f, _urlData + offset, _urlDataLength - offset); #ifdef WIN32 // M$Windows do a CR-LF translation, number of read byte is reduced // under M$Windows bytes == 0 is EOF if (bytes == 0) _urlDataLength = offset; #endif if (bytes < 0) { char msg[1024]; mysnprintf(msg,1023,"previous EOF in file %s", _editorFile); swMessageBox(TheApp->mainWnd(), msg, "dune: Edit Script", SW_MB_OK, SW_MB_WARNING); return; } offset += bytes; } while (offset < _urlDataLength); MFString* newUrl = new MFString(); if (ecmaScriptCheckEditorData()) { if (_urlStartData.size() != _urlEndData.size()) { char msg[1024]; mysnprintf(msg,1023,"missing \" \" pair in file %s ?", _editorFile); swMessageBox(TheApp->mainWnd(), msg, "dune: Edit Script", SW_MB_OK, SW_MB_WARNING); ecmaScriptStartEditor(); return; } for (int i = 0 ; i < _urlStartData.size(); i++) { _urlData[_urlEndData[i]] = '\0'; MyString urlString = ""; urlString += (&_urlData[_urlStartData[i]+1]); newUrl->setSFValue(i, new SFString(urlString)); } _scriptNode->setField(SCRIPT_URL_FIELD,newUrl); } else { ecmaScriptStartEditor(); return; } } // check for valid " qouting in data from editor // valid is // // blank_or_tab " some_text " // (multiple) continued with // blank_or_tab , blank_or_tab " some_text " // // blank_or_tab and some_text may be empty // \" is not counted as " static bool isDoubleQuoute(char* data, int offset) { if (offset == 0) return data[offset] == '"'; else return (data[offset] == '"') && (data[offset-1] != '\\'); } static bool isBlankOrTab(char* data, int offset) { return ((data[offset] == ' ') || (data[offset] == '\t')); } static bool isLineFeed(char* data, int offset) { return (data[offset] == '\n') || (data[offset+1] == '\r'); } static bool is2CharLineFeed(char* data, int offset) { if ( ((data[offset] == '\n') && (data[offset+1] == '\r')) || ((data[offset] == '\r') && (data[offset+1] == '\n')) ) return true; else return false; } enum { blankOrTabMode, someTextMode }; // additionly to check for this tokens, ecmaScriptCheckEditorData // also checks for beginning and end of the Script url field(s) (type MFString) bool ScriptEdit::ecmaScriptCheckEditorData(void) { int mode = blankOrTabMode; int lineCount = 0; int charsPerLine = 0; bool beforeFirstDoubleQuoute = true; _urlStartData.resize(0); _urlEndData.resize(0); for (int offset = 0; offset < _urlDataLength; offset++) { charsPerLine++; if (isLineFeed(_urlData,offset)) { if (is2CharLineFeed(_urlData, offset)) offset++; lineCount++; charsPerLine = 0; } else if (mode == blankOrTabMode) { bool invalidChar = false; if (!isBlankOrTab(_urlData,offset)) if (_urlData[offset] == ',') { if (beforeFirstDoubleQuoute) invalidChar = true; } else if (isDoubleQuoute(_urlData,offset)) { mode = someTextMode; _urlStartData.append(offset); } else invalidChar = true; if (invalidChar) { char msg[1024]; mysnprintf(msg,1023,"invalid char: line %d character %d", lineCount,charsPerLine); swMessageBox(TheApp->mainWnd(), msg, "dune error: Script data", SW_MB_OK, SW_MB_WARNING); return false; } } else if (isDoubleQuoute(_urlData,offset)) { mode = blankOrTabMode; beforeFirstDoubleQuoute = false; _urlEndData.append(offset); } } if (mode == someTextMode) { char msg[1024]; mysnprintf(msg,1023,"missing closing \" : line %d character %d" , lineCount,charsPerLine); swMessageBox(TheApp->mainWnd(), msg, "dune error: Script data", SW_MB_OK, SW_MB_WARNING); return false; } else return true; }