Logo Search packages:      
Sourcecode: whitedune version File versions  Download package

ScriptEdit.cpp

/*
 * 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 <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#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;   
}


Generated by  Doxygen 1.6.0   Back to index