POV-Ray : Newsgroups : povray.general : bug & bugfix - Pre_Frame_Return not working as expected : bug & bugfix - Pre_Frame_Return not working as expected Server Time
3 Aug 2024 22:13:22 EDT (-0400)
  bug & bugfix - Pre_Frame_Return not working as expected  
From: Albert Moben
Date: 19 Sep 2003 19:13:46
Message: <3f6b8daa@news.povray.org>
Here is a bug (and a bugfix) for a problem with the "Shell
Command Return Actions" (see 5.2.4.3 of the helpfile). These
are the INI-options Pre_Frame_Return=, Post_Frame_Return=,
Pre_Scene_Return= etc. It problem affects all these options,
but in the following examples I refer only to Pre_Frame_Return
for reasons of simplicity. I experienced the following problem
on Windows 2000 running the precompiled version of PoV 3.5,
but looking at the source code other platforms could
experience the same problems.


*** What PoV *should* do (see 5.2.4.3 of PoV-3.5 docs):

According to the documentation, the Pre_Frame_Return=S option
should work together with the Pre_Frame_Command=commandline
option. Pre_Frame_Command runs an external command before each
frame, and Pre_Frame_Return=... specifies what action PoV
should perform after the external application has finished
based on the return code of that external application. For
example, Pre_Frame_Return=S should skip the next frame of an
animation if the return code from the external application is
non-zero. For external applications which return zero instead
of non-zero to indicate success, we should specify "-S"
instead of "S" to skip the next frame for zero return values.


*** What PoV does (I think it's a BUG!):

Unfortunately the INI-option Pre_Frame_Return=S doesn't work
(the Post_... and _Scene_ variations also don't work, I take
Pre_Frame_... as an example to illustrate the problem). The
problem is this:

Pre_Frame_Return is only useful when combined with
Pre_Frame_Command. Because of a bug in the INI-parser,
whenever a Pre_Frame_Return option is encountered, a
commandline previously set with Pre_Frame_Command is reset to
an empty string. So if you place Pre_Frame_Return after
Pre_Frame_Command in the INI-file, the external application is
never called, because Pre_Frame_Return clears the commandline
for it. Just swapping the INI-options doesn't work either: If
you put Pre_Frame_Return first and Pre_Frame_Command second,
when the parser parses the Pre_Frame_Command option, it resets
the return-action to "I" (ignore return value), so the
external application is called, but the return value is always
ignored (thus, actions like skip-frame or abort-scene are
never carried out). The bugfix below will fix this.

There is also a second bug in the original sourcecode of PoV
3.5: According to the docs, preceeding the Pre_Frame_Return
option with a "-" or "!" should carry out the action if the
return value is zero (instead of non-zero), so
"Pre_Frame_Return=-S" would skip the frame if the commandline
of "Pre_Frame_Command=commandline" returns zero. Unfortunately
the parser completely fails to recognize the "-" or "!" prefix
to the option (only the first character of the parameter is
examined to be a letter S,A,F,etc., but something like "-S" is
two characters long, the parser fails to see this). I wonder
how this was ever meant to work. Probably the option-parsing-
code has undergone major changes in version 3.5. Anyway, the
bugfix shown below fixes this also.


*** Bugfix:

The following few lines must be replaced/added to two
sourcefiles in order to make it work as expected. To easier
find the lines, I show the original code and the modified
code.


mofification 1: "optin.cpp", function process_ini_option,
estimated lines 1234 to 1255, remove this code:

    case kPOVAttrib_FatalErrorCommand:
    case kPOVAttrib_PostFrameCommand:
    case kPOVAttrib_PostSceneCommand:
    case kPOVAttrib_PreFrameCommand:
    case kPOVAttrib_PreSceneCommand:
    case kPOVAttrib_UserAbortCommand:
        POVMSObject cmdobj;
        err = POVMSObject_New(&cmdobj, kPOVMSType_WildCard);
        if(toupper(*(option->keyword + strlen(option->keyword) - 1)) == 'D')
        {
            if(err == kNoErr)
                err = POVMSUtil_SetString(&cmdobj, kPOVAttrib_CommandString, param);
        }
        else
        {
            if(err == kNoErr)
                err = POVMSUtil_SetInt(&cmdobj, kPOVAttrib_ReturnAction,
tolower(*param));
        }
        if(err == kNoErr)
            err = POVMSObject_Set(obj, &cmdobj, option->key);
        break;

and replace it with this code:

    case kPOVAttrib_FatalErrorCommand:
    case kPOVAttrib_PostFrameCommand:
    case kPOVAttrib_PostSceneCommand:
    case kPOVAttrib_PreFrameCommand:
    case kPOVAttrib_PreSceneCommand:
    case kPOVAttrib_UserAbortCommand:
        POVMSObject cmdobj;
        // did we a previous option for this kind of command?
        err = POVMSObject_Exist(obj, option->key);
        if(err == kNoErr)
        {
            // There is already an object for that kind of command, this
            // happens if both ..._Command and ..._Return options are
            // set in the Ini-file, for example a Pre_Frame_Command=...
            // followed by a Pre_Frame_Return=S to evaluate the return
            // value of the command. Now get the object for that command
            // and modify it later.
            err = POVMSObject_Get(obj, &cmdobj, option->key);
            // POVMSObject_Get could fail despite the fact that we
            // called POVMSObject_Exist before, for example if it
            // runs out of memory, so we have to check here.
            if(err != kNoErr)
                break;
        }
        else if(err != kFalseErr) // POVMSObject_Exist failed with an
            break;                // error, just exist with that error.
        else // object doesn't exist for this command, create a new one:
            err = POVMSObject_New(&cmdobj, kPOVMSType_WildCard);
        if(toupper(*(option->keyword + strlen(option->keyword) - 1)) == 'D')
        {
            if(err == kNoErr)
                err = POVMSUtil_SetString(&cmdobj, kPOVAttrib_CommandString, param);
        }
        else
        {
            if(err == kNoErr)
            {
                int iReturnAction;
                // Get the return-action character from the parameter
                // Just the character, ignore - or ! prefix here
                if((*param == '-') || (*param == '!'))
                    iReturnAction = (int)tolower(param[1]);
                else
                    iReturnAction = tolower(*param);
                // Do some primitive error checking
                // (I could do better given more time...)
                if((iReturnAction != 'i')
                    && (iReturnAction != 'q')
                    && (iReturnAction != 'u')
                    && (iReturnAction != 'f')
                    && (iReturnAction != 's')
                    && (iReturnAction != 'a'))
                {
                    PossibleError("Unknown return action in %s=%s", option->keyword,
param);
                    err = kParamErr;
                    break;
                }
                // Encode the - or ! prefix to iReturnAction (change sign). This
                // is later decoded in SetCommandOption in povmsrec.cpp
                if((*param == '-') || (*param == '!'))
                    iReturnAction = -iReturnAction;
                err = POVMSUtil_SetInt(&cmdobj, kPOVAttrib_ReturnAction,
iReturnAction);
            }
        }
        if(err == kNoErr)
            err = POVMSObject_Set(obj, &cmdobj, option->key);
        break;


mofification 2: "optin.cpp", function output_ini_option,
estimated lines 604 to 608, remove this code:

    if(POVMSUtil_GetInt(&cmdobj, kPOVAttrib_ReturnAction, &intval) == 0)
    {
        chr = intval;
        file->printf("%s=%c\n", option->keyword, chr);
    }

and replace it with this code:

    if(POVMSUtil_GetInt(&cmdobj, kPOVAttrib_ReturnAction, &intval) == 0)
    {
        if(intval < 0)
        {
            chr = -intval;
            file->printf("%s=-%c\n", option->keyword, chr);
        }
        else
        {
            chr = intval;
            file->printf("%s=%c\n", option->keyword, chr);
        }
    }


mofification 3: "povmsrec.cpp", function SetCommandOption, at
line 137 add a few lines (shown in context):

    int ret = 0;

    err = POVMSUtil_GetInt(&obj, kPOVAttrib_ReturnAction, &ret);
    if(err == 0)
    {
        // HERE BEGINS ADDED CODE >>>>
        if(ret < 0)
        {
            data->Inverse = true;
            ret = -ret;
        }
        else
            data->Inverse = false;
        // <<<< HERE ENDS ADDED CODE
        switch(ret)
        {


That should fix the problem. Comments welcome.


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.