LCOV - code coverage report
Current view: top level - progs - msequencer.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 1962 0
Test Date: 2025-11-11 10:26:08 Functions: 0.0 % 32 0

            Line data    Source code
       1              : /********************************************************************\
       2              : 
       3              :   Name:         msequencer.cxx
       4              :   Created by:   Stefan Ritt
       5              : 
       6              :   Contents:     MIDAS sequencer engine
       7              : 
       8              : \********************************************************************/
       9              : 
      10              : #include "midas.h"
      11              : #include "msystem.h"
      12              : #include "mvodb.h"
      13              : #include "mxml.h"
      14              : #include "sequencer.h"
      15              : #include "mstrlcpy.h"
      16              : #include "tinyexpr.h"
      17              : #include "odbxx.h"
      18              : #include <assert.h>
      19              : #include <iostream>
      20              : #include <fstream>
      21              : #include <sstream>
      22              : #include <string.h>
      23              : #include <vector>
      24              : 
      25              : #define XNAME_LENGTH 256
      26              : 
      27              : /**dox***************************************************************/
      28              : /** @file sequencer.cxx
      29              : The Midas Sequencer file
      30              : */
      31              : 
      32              : /** @defgroup seqfunctioncode Sequencer Functions
      33              :  */
      34              : 
      35              : /**dox***************************************************************/
      36              : /** @addtogroup seqfunctioncode
      37              :  *
      38              :  *  @{  */
      39              : 
      40              : /*------------------------------------------------------------------*/
      41              : 
      42              : SEQUENCER_STR(sequencer_str);
      43              : 
      44              : MVOdb *gOdb = NULL;
      45              : 
      46              : // sequencer context
      47              : 
      48              : class SeqCon
      49              : {
      50              : public:
      51              :    PMXML_NODE pnseq = NULL;
      52              :    MVOdb *odbs = NULL;
      53              :    SEQUENCER *seqp = NULL;
      54              :    HNDLE hDB  = 0;
      55              :    HNDLE hSeq = 0;
      56              :    std::string seq_name;
      57              :    std::string prg_name;
      58              :    std::string odb_path;
      59              : };
      60              : 
      61              : /*------------------------------------------------------------------*/
      62              : 
      63            0 : char *stristr(const char *str, const char *pattern) {
      64              :    char c1, c2, *ps, *pp;
      65              : 
      66            0 :    if (str == NULL || pattern == NULL)
      67            0 :       return NULL;
      68              : 
      69            0 :    while (*str) {
      70            0 :       ps = (char *) str;
      71            0 :       pp = (char *) pattern;
      72            0 :       c1 = *ps;
      73            0 :       c2 = *pp;
      74            0 :       if (toupper(c1) == toupper(c2)) {
      75            0 :          while (*pp) {
      76            0 :             c1 = *ps;
      77            0 :             c2 = *pp;
      78              : 
      79            0 :             if (toupper(c1) != toupper(c2))
      80            0 :                break;
      81              : 
      82            0 :             ps++;
      83            0 :             pp++;
      84              :          }
      85              : 
      86            0 :          if (!*pp)
      87            0 :             return (char *) str;
      88              :       }
      89            0 :       str++;
      90              :    }
      91              : 
      92            0 :    return NULL;
      93              : }
      94              : 
      95              : /*------------------------------------------------------------------*/
      96              : 
      97            0 : void strsubst(char *string, int size, const char *pattern, const char *subst)
      98              : /* subsitute "pattern" with "subst" in "string" */
      99              : {
     100              :    char *tail, *p;
     101              :    int s;
     102              : 
     103            0 :    p = string;
     104            0 :    for (p = stristr(p, pattern); p != NULL; p = stristr(p, pattern)) {
     105              : 
     106            0 :       if (strlen(pattern) == strlen(subst)) {
     107            0 :          memcpy(p, subst, strlen(subst));
     108            0 :       } else if (strlen(pattern) > strlen(subst)) {
     109            0 :          memcpy(p, subst, strlen(subst));
     110            0 :          memmove(p + strlen(subst), p + strlen(pattern), strlen(p + strlen(pattern)) + 1);
     111              :       } else {
     112            0 :          tail = (char *) malloc(strlen(p) - strlen(pattern) + 1);
     113            0 :          strcpy(tail, p + strlen(pattern));
     114            0 :          s = size - (p - string);
     115            0 :          mstrlcpy(p, subst, s);
     116            0 :          mstrlcat(p, tail, s);
     117            0 :          free(tail);
     118            0 :          tail = NULL;
     119              :       }
     120              : 
     121            0 :       p += strlen(subst);
     122              :    }
     123            0 : }
     124              : 
     125              : /*------------------------------------------------------------------*/
     126              : 
     127            0 : static std::string toString(int v) {
     128              :    char buf[256];
     129            0 :    snprintf(buf, sizeof(buf), "%d", v);
     130            0 :    return buf;
     131              : }
     132              : 
     133            0 : static std::string qtoString(std::string filename, int line, int level) {
     134            0 :    std::string buf = "l=\"" + std::to_string(line) + "\"";
     135            0 :    buf += " fn=\"" + filename + "\"";
     136            0 :    if (level)
     137            0 :       buf += " lvl=\"" + std::to_string(level) + "\"";
     138            0 :    return buf;
     139            0 : }
     140              : 
     141            0 : static std::string q(const char *s) {
     142            0 :    return "\"" + std::string(s) + "\"";
     143              : }
     144              : 
     145              : /*------------------------------------------------------------------*/
     146              : 
     147            0 : bool is_valid_number(const char *str) {
     148            0 :    std::string s(str);
     149            0 :    std::stringstream ss;
     150            0 :    ss << s;
     151            0 :    double num = 0;
     152            0 :    ss >> num;
     153            0 :    if (ss.good())
     154            0 :       return false;
     155            0 :    else if (num == 0 && s[0] != '0')
     156            0 :       return false;
     157            0 :    else if (s[0] == 0)
     158            0 :       return false;
     159            0 :    return true;
     160            0 : }
     161              : 
     162              : /*------------------------------------------------------------------*/
     163              : 
     164            0 : void seq_error(SEQUENCER &seq, SeqCon* c, const char *str) {
     165              :    int status;
     166              :    HNDLE hKey;
     167              : 
     168            0 :    mstrlcpy(seq.error, str, sizeof(seq.error));
     169            0 :    seq.error_line = seq.current_line_number;
     170            0 :    seq.serror_line = seq.scurrent_line_number;
     171            0 :    seq.running = FALSE;
     172            0 :    seq.transition_request = FALSE;
     173              : 
     174            0 :    status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
     175            0 :    if (status != DB_SUCCESS)
     176            0 :       return;
     177            0 :    status = db_set_record(c->hDB, hKey, &seq, sizeof(seq), 0);
     178            0 :    if (status != DB_SUCCESS)
     179            0 :       return;
     180              : 
     181            0 :    cm_msg(MTALK, "sequencer", "Sequencer has stopped with error.");
     182              : }
     183              : 
     184              : /*------------------------------------------------------------------*/
     185              : 
     186            0 : int strbreak(char *str, char list[][XNAME_LENGTH], int size, const char *brk, BOOL ignore_quotes)
     187              : /* break comma-separated list into char array, stripping leading
     188              :  and trailing blanks */
     189              : {
     190              :    int i, j;
     191              :    char *p;
     192              : 
     193            0 :    memset(list, 0, size * XNAME_LENGTH);
     194            0 :    p = str;
     195            0 :    if (!p || !*p)
     196            0 :       return 0;
     197              : 
     198            0 :    while (*p == ' ')
     199            0 :       p++;
     200              : 
     201            0 :    for (i = 0; *p && i < size; i++) {
     202            0 :       if (*p == '"' && !ignore_quotes) {
     203            0 :          p++;
     204            0 :          j = 0;
     205            0 :          memset(list[i], 0, XNAME_LENGTH);
     206              :          do {
     207              :             /* convert two '"' to one */
     208            0 :             if (*p == '"' && *(p + 1) == '"') {
     209            0 :                list[i][j++] = '"';
     210            0 :                p += 2;
     211            0 :             } else if (*p == '"') {
     212            0 :                break;
     213              :             } else
     214            0 :                list[i][j++] = *p++;
     215              : 
     216            0 :          } while (j < XNAME_LENGTH - 1);
     217            0 :          list[i][j] = 0;
     218              : 
     219              :          /* skip second '"' */
     220            0 :          p++;
     221              : 
     222              :          /* skip blanks and break character */
     223            0 :          while (*p == ' ')
     224            0 :             p++;
     225            0 :          if (*p && strchr(brk, *p))
     226            0 :             p++;
     227            0 :          while (*p == ' ')
     228            0 :             p++;
     229              : 
     230              :       } else {
     231            0 :          mstrlcpy(list[i], p, XNAME_LENGTH);
     232              : 
     233            0 :          for (j = 0; j < (int) strlen(list[i]); j++)
     234            0 :             if (strchr(brk, list[i][j])) {
     235            0 :                list[i][j] = 0;
     236            0 :                break;
     237              :             }
     238              : 
     239            0 :          p += strlen(list[i]);
     240            0 :          while (*p == ' ')
     241            0 :             p++;
     242            0 :          if (*p && strchr(brk, *p))
     243            0 :             p++;
     244            0 :          while (*p == ' ')
     245            0 :             p++;
     246              : 
     247            0 :          while (list[i][strlen(list[i]) - 1] == ' ')
     248            0 :             list[i][strlen(list[i]) - 1] = 0;
     249              :       }
     250              : 
     251            0 :       if (!*p)
     252            0 :          break;
     253              :    }
     254              : 
     255            0 :    if (i == size)
     256            0 :       return size;
     257              : 
     258            0 :    return i + 1;
     259              : }
     260              : 
     261              : /*------------------------------------------------------------------*/
     262              : 
     263              : extern char *stristr(const char *str, const char *pattern);
     264              : 
     265              : /*------------------------------------------------------------------*/
     266              : 
     267            0 : std::string eval_var(SEQUENCER &seq, SeqCon* c, std::string value) {
     268            0 :    std::string result;
     269            0 :    std::string xpath;
     270            0 :    xpath += "/";
     271            0 :    xpath += c->odb_path;
     272              : 
     273              :    //printf("eval [%s] xpath [%s]\n", value.c_str(), xpath.c_str());
     274              : 
     275            0 :    result = value;
     276              : 
     277              :    // replace all $... with value
     278              :    int i1, i2;
     279            0 :    std::string vsubst;
     280            0 :    while ((i1 = (int)result.find("$")) != (int)std::string::npos) {
     281            0 :       std::string s = result.substr(i1 + 1);
     282            0 :       if (std::isdigit(s[0])) {
     283              :          // find end of number
     284            0 :          for (i2 = i1 + 1; std::isdigit(result[i2]);)
     285            0 :             i2++;
     286              : 
     287              :          // replace all $<number> with subroutine parameters
     288            0 :          int index = atoi(s.c_str());
     289            0 :          if (seq.stack_index > 0) {
     290            0 :             std::istringstream f(seq.subroutine_param[seq.stack_index - 1]);
     291            0 :             std::vector<std::string> param;
     292            0 :             std::string sp;
     293            0 :             while (std::getline(f, sp, ','))
     294            0 :                param.push_back(sp);
     295            0 :             if (index == 0 || index > (int)param.size())
     296            0 :                throw "Parameter \"$" + std::to_string(index) + "\" not valid";
     297            0 :             vsubst = param[index - 1];
     298            0 :             if (vsubst[0] == '$')
     299            0 :                vsubst = eval_var(seq, c, vsubst);
     300            0 :          } else
     301            0 :             throw "Parameter \"$" + std::to_string(index) + "\" not valid";
     302              :       } else {
     303              :          // find end of string
     304            0 :          for (i2 = i1 + 1; std::isalnum(result[i2]) || result[i2] == '_';)
     305            0 :             i2++;
     306            0 :          s = s.substr(0, i2 - i1 - 1);
     307            0 :          if (result[i2] == '[') {
     308              :             // array
     309            0 :             auto sindex = result.substr(i2+1);
     310            0 :             int i = sindex.find(']');
     311            0 :             if (i == (int)std::string::npos)
     312            0 :                throw "Variable \"" + result +"\" does not contain ']'";
     313            0 :             sindex = sindex.substr(0, i);
     314            0 :             sindex = eval_var(seq, c, sindex);
     315              :             int index;
     316              :             try {
     317            0 :                index = std::stoi(sindex);
     318            0 :             } catch (...) {
     319            0 :                throw "Variable \"" + s + "\" has invalid index";
     320            0 :             }
     321              : 
     322              :             try {
     323            0 :                midas::odb o(xpath + "/Variables/" + s);
     324            0 :                std::vector<std::string> sv = o;
     325            0 :                vsubst = sv[index];
     326            0 :             } catch (...) {
     327            0 :                throw "Variable \"" + s + "\" not found";
     328            0 :             }
     329            0 :             while (result[i2] && result[i2] != ']')
     330            0 :                i2++;
     331            0 :             if (!result[i2])
     332            0 :                throw "Variable \"" + result +"\" does not contain ']'";
     333            0 :             if (result[i2] == ']')
     334            0 :                i2++;
     335            0 :          } else {
     336              :             try {
     337            0 :                if (!midas::odb::exists(xpath + "/Variables/" + s))
     338            0 :                   throw "Variable \"" + s + "\" not found";
     339            0 :                midas::odb o(xpath + "/Variables/" + s);
     340            0 :                vsubst = o;
     341            0 :             } catch (...) {
     342            0 :                throw "Variable \"" + s + "\" not found";
     343            0 :             }
     344              :          }
     345              :       }
     346              : 
     347            0 :       result = result.substr(0, i1) + vsubst + result.substr(i2);
     348            0 :    }
     349              : 
     350              :    //printf("eval [%s] xpath [%s] result [%s]\n", value.c_str(), xpath.c_str(), result.c_str());
     351              : 
     352              :    // check if result is a list
     353            0 :    if (result.find(",") != std::string::npos)
     354            0 :       return result;
     355              : 
     356              :    // check for expression
     357              :    int error;
     358            0 :    double r = te_interp(result.c_str(), &error);
     359            0 :    if (error > 0) {
     360              :       // check if result is only a string
     361            0 :       if (!std::isdigit(result[0]) && result[0] != '-')
     362            0 :          return result;
     363              : 
     364            0 :       throw "Error in expression \"" + result + "\" position " + std::to_string(error - 1);
     365              :    }
     366              : 
     367            0 :    if (r == (int) r)
     368            0 :       return std::to_string((int) r);
     369              : 
     370            0 :    return std::to_string(r);
     371            0 : }
     372              : 
     373              : /*------------------------------------------------------------------*/
     374              : 
     375            0 : int concatenate(SEQUENCER &seq, SeqCon* c, char *result, int size, char *value) {
     376              :    char list[100][XNAME_LENGTH];
     377              :    int i, n;
     378              : 
     379            0 :    n = strbreak(value, list, 100, ",", FALSE);
     380              : 
     381            0 :    result[0] = 0;
     382            0 :    for (i = 0; i < n; i++) {
     383            0 :       std::string str = eval_var(seq, c, std::string(list[i]));
     384            0 :       mstrlcat(result, str.c_str(), size);
     385            0 :    }
     386              : 
     387            0 :    return TRUE;
     388              : }
     389              : 
     390              : /*------------------------------------------------------------------*/
     391              : 
     392            0 : int eval_condition(SEQUENCER &seq, SeqCon* c, const char *condition) {
     393              :    int i;
     394              :    double value1, value2;
     395              :    char value1_str[256], value2_str[256], str[256], op[3], *p;
     396            0 :    std::string value1_var, value2_var;
     397              : 
     398              :    // strip leading and trailing space
     399            0 :    p = (char *)condition;
     400            0 :    while (*p == ' ')
     401            0 :       p++;
     402            0 :    strcpy(str, p);
     403              : 
     404              :    // strip any comment '#'
     405            0 :    if (strchr(str, '#'))
     406            0 :       *strchr(str, '#') = 0;
     407              : 
     408            0 :    while (strlen(str) > 0 && (str[strlen(str)-1] == ' '))
     409            0 :       str[strlen(str)-1] = 0;
     410              : 
     411              :    // strip enclosing '()'
     412            0 :    if (str[0] == '(' && strlen(str) > 0 && str[strlen(str)-1] == ')') {
     413            0 :       mstrlcpy(value1_str, str+1, sizeof(value1_str));
     414            0 :       mstrlcpy(str, value1_str, sizeof(str));
     415            0 :       str[strlen(str)-1] = 0;
     416              :    }
     417              : 
     418            0 :    op[1] = op[2] = 0;
     419              : 
     420              :    /* find value and operator */
     421            0 :    for (i = 0; i < (int) strlen(str); i++)
     422            0 :       if (strchr("<>=!&", str[i]) != NULL)
     423            0 :          break;
     424            0 :    mstrlcpy(value1_str, str, i + 1);
     425            0 :    while (value1_str[strlen(value1_str) - 1] == ' ')
     426            0 :       value1_str[strlen(value1_str) - 1] = 0;
     427            0 :    op[0] = str[i];
     428            0 :    if (strchr("<>=!&", str[i + 1]) != NULL)
     429            0 :       op[1] = str[++i];
     430              : 
     431            0 :    for (i++; str[i] == ' '; i++);
     432            0 :    mstrlcpy(value2_str, str + i, sizeof(value2_str));
     433              : 
     434            0 :    value1_var = eval_var(seq, c, value1_str);
     435            0 :    value2_var = eval_var(seq, c, value2_str);
     436              : 
     437            0 :    if (!is_valid_number(value1_var.c_str()) || !is_valid_number(value2_var.c_str())) {
     438              :       // string comparison
     439            0 :       if (strcmp(op, "=") == 0)
     440            0 :          return equal_ustring(value1_var.c_str(), value2_var.c_str()) ? 1 : 0;
     441            0 :       if (strcmp(op, "==") == 0)
     442            0 :          return equal_ustring(value1_var.c_str(), value2_var.c_str()) ? 1 : 0;
     443            0 :       if (strcmp(op, "!=") == 0)
     444            0 :          return equal_ustring(value1_var.c_str(), value2_var.c_str()) ? 0 : 1;
     445              :       // invalid operator for string comparisons
     446            0 :       return -1;
     447              :    }
     448              : 
     449              :    // numeric comparison
     450            0 :    value1 = atof(value1_var.c_str());
     451            0 :    value2 = atof(value2_var.c_str());
     452              : 
     453              :    /* now do logical operation */
     454            0 :    if (strcmp(op, "=") == 0)
     455            0 :       if (value1 == value2)
     456            0 :          return 1;
     457            0 :    if (strcmp(op, "==") == 0)
     458            0 :       if (value1 == value2)
     459            0 :          return 1;
     460            0 :    if (strcmp(op, "!=") == 0)
     461            0 :       if (value1 != value2)
     462            0 :          return 1;
     463            0 :    if (strcmp(op, "<") == 0)
     464            0 :       if (value1 < value2)
     465            0 :          return 1;
     466            0 :    if (strcmp(op, ">") == 0)
     467            0 :       if (value1 > value2)
     468            0 :          return 1;
     469            0 :    if (strcmp(op, "<=") == 0)
     470            0 :       if (value1 <= value2)
     471            0 :          return 1;
     472            0 :    if (strcmp(op, ">=") == 0)
     473            0 :       if (value1 >= value2)
     474            0 :          return 1;
     475            0 :    if (strcmp(op, "&") == 0)
     476            0 :       if (((unsigned int) value1 & (unsigned int) value2) > 0)
     477            0 :          return 1;
     478              : 
     479            0 :    return 0;
     480            0 : }
     481              : 
     482              : /*------------------------------------------------------------------*/
     483              : 
     484            0 : bool loadMSL(MVOdb *o, const std::string &filename) {
     485              : 
     486            0 :    std::ifstream file(filename);
     487            0 :    if (!file.is_open())
     488            0 :       return false;
     489              : 
     490            0 :    std::vector<std::string> lines;
     491            0 :    std::string line;
     492              : 
     493              :    // read all lines from file and put it into array
     494            0 :    while (std::getline(file, line))
     495            0 :       lines.push_back(line);
     496            0 :    file.close();
     497              : 
     498            0 :    o->WSA("Script/Lines", lines, 0);
     499              : 
     500            0 :    return true;
     501            0 : }
     502              : 
     503              : /*------------------------------------------------------------------*/
     504              : 
     505            0 : static BOOL msl_parse(SEQUENCER& seq, SeqCon*c, int level, const char *cpath, const char *filename, const char *xml_filename,
     506              :                       char *error, int error_size, int *error_line) {
     507              :    char str[256], *pl, *pe;
     508              :    char list[100][XNAME_LENGTH], list2[100][XNAME_LENGTH], **lines;
     509              :    int i, j, n, nq, n_lines, endl, line, nest, incl, library;
     510            0 :    std::string xml, path, fn;
     511              :    char *msl_include, *xml_include, *include_error;
     512              :    int include_error_size;
     513              :    BOOL include_status, rel_path;
     514              : 
     515            0 :    if (level > 10) {
     516            0 :       snprintf(error, error_size, "More than 10 nested INCLUDE statements exceeded in file \"%s\"", filename);
     517            0 :       return FALSE;
     518              :    }
     519              : 
     520            0 :    rel_path = (filename[0] != DIR_SEPARATOR);
     521            0 :    path = std::string(cpath);
     522            0 :    if (path.length() > 0 && path.back() != '/')
     523            0 :       path += '/';
     524              : 
     525            0 :    std::string fullFilename;
     526            0 :    if (rel_path)
     527            0 :       fullFilename = path + filename;
     528              :    else
     529            0 :       fullFilename = filename;
     530              : 
     531            0 :    int fhin = open(fullFilename.c_str(), O_RDONLY | O_TEXT);
     532            0 :    if (fhin < 0) {
     533            0 :       snprintf(error, error_size, "Cannot open \"%s\", errno %d (%s)", fullFilename.c_str(), errno, strerror(errno));
     534            0 :       return FALSE;
     535              :    }
     536              : 
     537            0 :    off_t off = lseek(fhin, 0, SEEK_END);
     538            0 :    if (off < 0) {
     539            0 :       snprintf(error, error_size, "Cannot find size of \"%s\", lseek(SEEK_END) errno %d (%s)",  fullFilename.c_str(), errno, strerror(errno));
     540            0 :       close(fhin);
     541            0 :       return FALSE;
     542              :    }
     543              : 
     544            0 :    lseek(fhin, 0, SEEK_SET);
     545              : 
     546            0 :    std::string s;
     547            0 :    if (rel_path)
     548            0 :       s = path + xml_filename;
     549              :    else
     550            0 :       s = xml_filename;
     551              : 
     552            0 :    FILE *fout = fopen(s.c_str(), "wt");
     553            0 :    if (fout == NULL) {
     554            0 :       snprintf(error, error_size, "Cannot write to \"%s\", fopen() errno %d (%s)", s.c_str(), errno, strerror(errno));
     555            0 :       close(fhin);
     556            0 :       return FALSE;
     557              :    }
     558              : 
     559            0 :    size_t size = off;
     560              : 
     561            0 :    char* buf = (char *) malloc(size + 1);
     562            0 :    ssize_t rd = read(fhin, buf, size);
     563              :    
     564            0 :    if (rd < 0) {
     565            0 :       snprintf(error, error_size, "Cannot read \"%s\", read(%zu) errno %d (%s)",  fullFilename.c_str(), size, errno, strerror(errno));
     566            0 :       size = 0;
     567              :    } else {
     568            0 :       size = rd;
     569              :    }
     570              : 
     571            0 :    buf[size] = 0;
     572            0 :    close(fhin);
     573              : 
     574              :    /* look for any includes */
     575            0 :    lines = (char **) malloc(sizeof(char *));
     576            0 :    incl = 0;
     577            0 :    pl = buf;
     578            0 :    library = FALSE;
     579            0 :    for (n_lines = 0; *pl; n_lines++) {
     580            0 :       lines = (char **) realloc(lines, sizeof(char *) * (n_lines + 1));
     581            0 :       lines[n_lines] = pl;
     582            0 :       if (strchr(pl, '\n')) {
     583            0 :          pe = strchr(pl, '\n');
     584            0 :          *pe = 0;
     585            0 :          if (*(pe - 1) == '\r') {
     586            0 :             *(pe - 1) = 0;
     587              :          }
     588            0 :          pe++;
     589              :       } else
     590            0 :          pe = pl + strlen(pl);
     591            0 :       mstrlcpy(str, pl, sizeof(str));
     592            0 :       pl = pe;
     593            0 :       strbreak(str, list, 100, ", ", FALSE);
     594            0 :       if (equal_ustring(list[0], "include")) {
     595            0 :          if (!incl) {
     596            0 :             xml += "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
     597            0 :             xml += "<!DOCTYPE RunSequence [\n";
     598            0 :             incl = 1;
     599              :          }
     600              : 
     601              :          // if a path is given, use filename as entity reference
     602            0 :          char *reference = strrchr(list[1], '/');
     603            0 :          if (reference)
     604            0 :             reference++;
     605              :          else
     606            0 :             reference = list[1];
     607              : 
     608            0 :          if (strchr(reference, '.') && !strstr(reference, ".msl") && !strstr(reference, ".MSL")) {
     609            0 :             snprintf(error, error_size, "Include file \"%s\" cannot have an extension other than .msl", reference);
     610            0 :             return FALSE;
     611              :          }
     612              : 
     613            0 :          std::string p;
     614            0 :          if (list[1][0] == '/') {
     615            0 :             p = std::string(cm_get_path());
     616            0 :             p += "userfiles/sequencer";
     617            0 :             p += list[1];
     618              :          } else {
     619            0 :             p = path;
     620            0 :             if (!p.empty() && p.back() != '/')
     621            0 :                p += "/";
     622            0 :             p += list[1];
     623              :          }
     624              : 
     625            0 :          xml += "  <!ENTITY ";
     626            0 :          xml += reference;
     627            0 :          xml += " SYSTEM \"";
     628            0 :          xml += p;
     629            0 :          xml += ".xml\">\n";
     630              : 
     631              :          // recurse
     632            0 :          size = p.length() + 1 + 4;
     633            0 :          msl_include = (char *) malloc(size);
     634            0 :          xml_include = (char *) malloc(size);
     635            0 :          mstrlcpy(msl_include, p.c_str(), size);
     636            0 :          mstrlcpy(xml_include, p.c_str(), size);
     637            0 :          if (!strstr(msl_include, ".msl"))
     638            0 :             mstrlcat(msl_include, ".msl", size);
     639            0 :          mstrlcat(xml_include, ".xml", size);
     640              : 
     641            0 :          include_error = error + strlen(error);
     642            0 :          include_error_size = error_size - strlen(error);
     643              : 
     644            0 :          std::string path(cm_get_path());
     645            0 :          path += "userfiles/sequencer/";
     646            0 :          path += seq.path;
     647              : 
     648            0 :          include_status = msl_parse(seq, c, level+1, path.c_str(), msl_include, xml_include, include_error, include_error_size, error_line);
     649            0 :          free(msl_include);
     650            0 :          free(xml_include);
     651              : 
     652            0 :          if (!include_status) {
     653              :             // report the error on CALL line instead of the one in included file
     654            0 :             *error_line = n_lines + 1;
     655            0 :             return FALSE;
     656              :          }
     657            0 :       }
     658            0 :       if (equal_ustring(list[0], "library")) {
     659            0 :          xml += "<Library name=\"";
     660            0 :          xml += list[1];
     661            0 :          xml += "\">\n";
     662            0 :          library = TRUE;
     663              :       }
     664              :    }
     665            0 :    if (incl) {
     666            0 :       xml += "]>\n";
     667            0 :    } else if (!library) {
     668            0 :       xml += "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
     669              :    }
     670              : 
     671              :    /* parse rest of file */
     672            0 :    if (!library) {
     673            0 :       xml += "<RunSequence xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"\">\n";
     674              :    }
     675              : 
     676            0 :    std::vector<std::string> slines;
     677            0 :    for (line = 0; line < n_lines; line++) {
     678            0 :       slines.push_back(lines[line]);
     679              :    }
     680              : 
     681            0 :    c->odbs->WSA("Script/Lines", slines, 0);
     682              : 
     683              :    /* save parameters */
     684            0 :    std::string p = "/" + c->odb_path + "/Param/Value";
     685            0 :    midas::odb oldSeqParam(p);
     686            0 :    oldSeqParam.set_auto_refresh_read(false);
     687            0 :    oldSeqParam.set_auto_create(false);
     688              : 
     689              :    /* clear all variables */
     690            0 :    if (!seq.running) {
     691            0 :       c->odbs->Delete("Variables");
     692            0 :       c->odbs->Delete("Param");
     693              :    }
     694              : 
     695            0 :    for (line = 0; line < n_lines; line++) {
     696            0 :       char *p = lines[line];
     697            0 :       while (*p == ' ')
     698            0 :          p++;
     699            0 :       mstrlcpy(list[0], p, sizeof(list[0]));
     700            0 :       if (strchr(list[0], ' '))
     701            0 :          *strchr(list[0], ' ') = 0;
     702            0 :       p += strlen(list[0]);
     703            0 :       n = strbreak(p + 1, &list[1], 99, ",", FALSE) + 1;
     704              : 
     705              :       /* remove any full comment line */
     706            0 :       for (i = 0; i < n; i++) {
     707            0 :          if (list[i][0] == '#') {
     708            0 :             for (j = i; j < n; j++)
     709            0 :                list[j][0] = 0;
     710            0 :             break;
     711              :          }
     712              :       }
     713              : 
     714              :       /* cut any partial comment line */
     715            0 :       for (i = 0; i < n; i++) {
     716            0 :          if (strchr(list[i], '#'))
     717            0 :             *strchr(list[i], '#') = 0;
     718              :       }
     719              : 
     720              :       /* strip any trailing blanks */
     721            0 :       for (i = 0; i < n; i++) {
     722            0 :          while (strlen(list[i]) > 0 && list[i][strlen(list[i])-1] == ' ')
     723            0 :             list[i][strlen(list[i])-1] = 0;
     724              :       }
     725              : 
     726              :       /* check for variable assignment */
     727              :       char eq[1024];
     728            0 :       mstrlcpy(eq, lines[line], sizeof(eq));
     729            0 :       if (strchr(eq, '#'))
     730            0 :          *strchr(eq, '#') = 0;
     731            0 :       for (i = 0, n = 0, nq = 0; i < (int)strlen(eq); i++) {
     732            0 :          if (eq[i] == '\"')
     733            0 :             nq = (nq == 0 ? 1 : 0);
     734            0 :          if (eq[i] == '=' && nq == 0 &&
     735            0 :              (i > 0 && (eq[i - 1] != '!') && eq[i - 1] != '<' && eq[i - 1] != '>'))
     736            0 :             n++;
     737              :       }
     738            0 :       if (n == 1 && eq[0] != '=') {
     739              :          // equation found
     740            0 :          mstrlcpy(list[0], "SET", sizeof(list[0]));
     741            0 :          p = eq;
     742            0 :          while (*p == ' ')
     743            0 :             p++;
     744            0 :          mstrlcpy(list[1], p, sizeof(list[1]));
     745            0 :          *strchr(list[1], '=') = 0;
     746            0 :          if (strchr(list[1], ' '))
     747            0 :             *strchr(list[1], ' ') = 0;
     748            0 :          p = strchr(eq, '=')+1;
     749            0 :          while (*p == ' ')
     750            0 :             p++;
     751            0 :          mstrlcpy(list[2], p, sizeof(list[2]));
     752            0 :          while (strlen(list[2]) > 0 && list[2][strlen(list[2])-1] == ' ')
     753            0 :             list[2][strlen(list[2])-1] = 0;
     754              :       }
     755              : 
     756            0 :       if (equal_ustring(list[0], "library")) {
     757              : 
     758            0 :       } else if (equal_ustring(list[0], "include")) {
     759              :          // if a path is given, use filename as entity reference
     760            0 :          char *reference = strrchr(list[1], '/');
     761            0 :          if (reference)
     762            0 :             reference++;
     763              :          else
     764            0 :             reference = list[1];
     765              : 
     766            0 :          xml += "&";
     767            0 :          xml += reference;
     768            0 :          xml += ";\n";
     769              : 
     770            0 :       } else if (equal_ustring(list[0], "call")) {
     771            0 :          xml += "<Call " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">";
     772            0 :          for (i = 2; i < 100 && list[i][0]; i++) {
     773            0 :             if (i > 2) {
     774            0 :                xml += ",";
     775              :             }
     776            0 :             xml += list[i];
     777              :          }
     778            0 :          xml += "</Call>\n";
     779              : 
     780            0 :       } else if (equal_ustring(list[0], "cat")) {
     781            0 :          xml += "<Cat " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">";
     782            0 :          for (i = 2; i < 100 && list[i][0]; i++) {
     783            0 :             if (i > 2) {
     784            0 :                xml += ",";
     785              :             }
     786            0 :             xml += q(list[i]);
     787              :          }
     788            0 :          xml += "</Cat>\n";
     789              : 
     790            0 :       } else if (equal_ustring(list[0], "comment")) {
     791            0 :          xml += "<Comment " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Comment>\n";
     792              : 
     793            0 :       } else if (equal_ustring(list[0], "exit")) {
     794            0 :          xml += "<Exit " + qtoString(fullFilename, line + 1, level) + " />\n";
     795              : 
     796            0 :       } else if (equal_ustring(list[0], "goto")) {
     797            0 :          xml += "<Goto " + qtoString(fullFilename, line + 1, level) + " sline=" + q(list[1]) + " />\n";
     798              : 
     799            0 :       } else if (equal_ustring(list[0], "if")) {
     800            0 :          xml += "<If " + qtoString(fullFilename, line + 1, level) + " condition=\"";
     801            0 :          for (i = 1; i < 100 && list[i][0] && stricmp(list[i], "THEN") != 0; i++) {
     802            0 :             xml += list[i];
     803              :          }
     804            0 :          xml += "\">\n";
     805              : 
     806            0 :       } else if (equal_ustring(list[0], "else")) {
     807            0 :          xml += "<Else />\n";
     808              : 
     809            0 :       } else if (equal_ustring(list[0], "endif")) {
     810            0 :          xml += "</If>\n";
     811              : 
     812            0 :       } else if (equal_ustring(list[0], "loop")) {
     813              :          /* find end of loop */
     814            0 :          for (i = line, nest = 0; i < n_lines; i++) {
     815            0 :             strbreak(lines[i], list2, 100, ", ", FALSE);
     816            0 :             if (equal_ustring(list2[0], "loop"))
     817            0 :                nest++;
     818            0 :             if (equal_ustring(list2[0], "endloop")) {
     819            0 :                nest--;
     820            0 :                if (nest == 0)
     821            0 :                   break;
     822              :             }
     823              :          }
     824            0 :          if (i < n_lines)
     825            0 :             endl = i + 1;
     826              :          else
     827            0 :             endl = line + 1;
     828            0 :          if (list[2][0] == 0) {
     829            0 :             xml += "<Loop " + qtoString(fullFilename, line + 1, level) + " le=\"" + std::to_string(endl) + "\" n=" + q(list[1]) + ">\n";
     830            0 :          } else if (list[3][0] == 0) {
     831            0 :             xml += "<Loop " + qtoString(fullFilename, line + 1, level) + " le=\"" + std::to_string(endl) + "\" var=" + q(list[1]) + " n=" +
     832            0 :                    q(list[2]) + ">\n";
     833              :          } else {
     834            0 :             xml += "<Loop " + qtoString(fullFilename, line + 1, level) + " le=\"" + std::to_string(endl) + "\" var=" + q(list[1]) + " values=\"";
     835            0 :             for (i = 2; i < 100 && list[i][0]; i++) {
     836            0 :                if (i > 2) {
     837            0 :                   xml += ",";
     838              :                }
     839            0 :                xml += list[i];
     840              :             }
     841            0 :             xml += "\">\n";
     842              :          }
     843            0 :       } else if (equal_ustring(list[0], "endloop")) {
     844            0 :          xml += "</Loop>\n";
     845              : 
     846            0 :       } else if (equal_ustring(list[0], "break")) {
     847            0 :          xml += "<Break "+ qtoString(fullFilename, line + 1, level) +"></Break>\n";
     848              : 
     849            0 :       } else if (equal_ustring(list[0], "message")) {
     850            0 :          xml += "<Message " + qtoString(fullFilename, line + 1, level);
     851            0 :          if (list[2][0] == '1')
     852            0 :             xml += " wait=\"1\"";
     853            0 :          xml += ">";
     854            0 :          xml += list[1];
     855            0 :          xml += "</Message>\n";
     856              : 
     857            0 :       } else if (equal_ustring(list[0], "msg")) {
     858            0 :          if (list[2][0]) {
     859            0 :             xml += "<Msg " + qtoString(fullFilename, line + 1, level) + " type=\""+list[2]+"\">" + list[1] + "</Msg>\n";
     860              :          } else {
     861            0 :             xml += "<Msg " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Msg>\n";
     862              :          }
     863              : 
     864            0 :       } else if (equal_ustring(list[0], "odbinc")) {
     865            0 :          if (list[2][0] == 0)
     866            0 :             mstrlcpy(list[2], "1", 2);
     867            0 :          xml += "<ODBInc " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBInc>\n";
     868              : 
     869            0 :       } else if (equal_ustring(list[0], "odbcreate")) {
     870            0 :          if (list[3][0]) {
     871            0 :             xml += "<ODBCreate " + qtoString(fullFilename, line + 1, level) + " size=" + q(list[3]) + " path=" + q(list[1]) + " type=" +
     872            0 :                    q(list[2]) + "></ODBCreate>\n";
     873              :          } else {
     874            0 :             xml += "<ODBCreate " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + " type=" + q(list[2]) +
     875            0 :                    "></ODBCreate>\n";
     876              :          }
     877              : 
     878            0 :       } else if (equal_ustring(list[0], "odbdelete")) {
     879            0 :          xml += "<ODBDelete " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</ODBDelete>\n";
     880              : 
     881            0 :       } else if (equal_ustring(list[0], "odbset")) {
     882            0 :          if (list[3][0]) {
     883            0 :             xml += "<ODBSet " + qtoString(fullFilename, line + 1, level) + " notify=" + q(list[3]) + " path=" + q(list[1]) + ">" +
     884            0 :                    list[2] + "</ODBSet>\n";
     885              :          } else {
     886            0 :             xml += "<ODBSet " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBSet>\n";
     887              :          }
     888              : 
     889            0 :       } else if (equal_ustring(list[0], "odbload")) {
     890            0 :          if (list[2][0]) {
     891            0 :             xml += "<ODBLoad " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[2]) + ">" + list[1] + "</ODBLoad>\n";
     892              :          } else {
     893            0 :             xml += "<ODBLoad " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</ODBLoad>\n";
     894              :          }
     895              : 
     896            0 :       } else if (equal_ustring(list[0], "odbget")) {
     897            0 :          xml += "<ODBGet " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBGet>\n";
     898              : 
     899            0 :       } else if (equal_ustring(list[0], "odblookup")) {
     900            0 :          xml += "<ODBLookup " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) +
     901            0 :             " string=" + q(list[2]) + ">" + list[3] + "</ODBLookup>\n";
     902              : 
     903            0 :       } else if (equal_ustring(list[0], "odbsave")) {
     904            0 :          xml += "<ODBSave " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">" + list[2] + "</ODBSave>\n";
     905              : 
     906            0 :       } else if (equal_ustring(list[0], "odbsubdir")) {
     907            0 :          if (list[2][0]) {
     908            0 :             xml += "<ODBSubdir " + qtoString(fullFilename, line + 1, level) + " notify=" + q(list[2]) + " path=" + q(list[1]) + ">\n";
     909              :          } else {
     910            0 :             xml += "<ODBSubdir " + qtoString(fullFilename, line + 1, level) + " path=" + q(list[1]) + ">\n";
     911              :          }
     912            0 :       } else if (equal_ustring(list[0], "endodbsubdir")) {
     913            0 :          xml += "</ODBSubdir>\n";
     914              : 
     915            0 :       } else if (equal_ustring(list[0], "param")) {
     916            0 :          if (list[2][0] == 0 || equal_ustring(list[2], "bool")) { // name and bool
     917            0 :             snprintf(error, error_size, "Parameter \"%s\" misses 'comment'", list[1]);
     918            0 :             *error_line = line + 1;
     919            0 :             return FALSE;
     920              : 
     921            0 :          } else if (!list[4][0] && equal_ustring(list[3], "bool")) { // name, comment and bool
     922            0 :             xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " type=\"bool\" " + " comment=" + q(list[2]) + "/>\n";
     923            0 :             bool v = false;
     924            0 :             c->odbs->RB((std::string("Param/Value/") + list[1]).c_str(), &v, true);
     925            0 :             c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
     926            0 :          } else if (!list[5][0] && equal_ustring(list[4], "bool")) { // name, comment, default and bool
     927            0 :             xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " type=\"bool\" default=" + q(list[4]) +"/>\n";
     928            0 :             std::string def(list[3]);
     929            0 :             bool v(def == "1" || def == "true" || def == "TRUE");
     930            0 :             c->odbs->RB((std::string("Param/Value/") + list[1]).c_str(), &v, true);
     931            0 :             c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
     932            0 :             c->odbs->WS((std::string("Param/Defaults/") + list[1]).c_str(), list[3]);
     933              : 
     934            0 :          } else if (!list[3][0]) { // name and comment
     935            0 :             xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " comment=" + q(list[2]) + " />\n";
     936            0 :             std::string v;
     937            0 :             c->odbs->RS((std::string("Param/Value/") + list[1]).c_str(), &v, true);
     938            0 :             c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
     939            0 :          } else if (!list[4][0]) { // name, comment and default
     940            0 :             xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " comment=" + q(list[2]) + " default=" + q(list[3]) + " />\n";
     941            0 :             std::string v(list[3]);
     942            0 :             c->odbs->RS((std::string("Param/Value/") + list[1]).c_str(), &v, true);
     943            0 :             c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
     944            0 :             c->odbs->WS((std::string("Param/Defaults/") + list[1]).c_str(), list[3]);
     945              :             // ss_sleep(1000); // use this for parameter test
     946            0 :          } else {
     947            0 :             xml += "<Param " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + " comment=" + q(list[2]) +
     948            0 :                    " options=\"";
     949            0 :             std::string v;
     950            0 :             c->odbs->RS((std::string("Param/Value/") + list[1]).c_str(), &v, true);
     951            0 :             c->odbs->WS((std::string("Param/Comment/") + list[1]).c_str(), list[2]);
     952            0 :             std::vector<std::string> options;
     953            0 :             for (i = 3; i < 100 && list[i][0]; i++) {
     954            0 :                if (i > 3) {
     955            0 :                   xml += ",";
     956              :                }
     957            0 :                xml += list[i];
     958            0 :                options.push_back(list[i]);
     959              :             }
     960            0 :             xml += "\" />\n";
     961            0 :             c->odbs->WSA((std::string("Param/Options/") + list[1]).c_str(), options, 0);
     962            0 :          }
     963              : 
     964              :          // put back old parameter value if existing
     965              :          try {
     966            0 :             std::string ov = oldSeqParam[(const char *)list[1]];
     967            0 :             c->odbs->WS((std::string("Param/Value/") + list[1]).c_str(), ov.c_str());
     968            0 :          } catch(mexception &e) {}
     969              : 
     970            0 :       } else if (equal_ustring(list[0], "rundescription")) {
     971            0 :          xml += "<RunDescription " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</RunDescription>\n";
     972              : 
     973            0 :       } else if (equal_ustring(list[0], "script")) {
     974            0 :          if (list[2][0] == 0) {
     975            0 :             xml += "<Script " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Script>\n";
     976              :          } else {
     977            0 :             xml += "<Script " + qtoString(fullFilename, line + 1, level) + " params=\"";
     978            0 :             for (i = 2; i < 100 && list[i][0]; i++) {
     979            0 :                if (i > 2) {
     980            0 :                   xml += ",";
     981              :                }
     982            0 :                xml += list[i];
     983              :             }
     984            0 :             xml += "\">";
     985            0 :             xml += list[1];
     986            0 :             xml += "</Script>\n";
     987              :          }
     988              : 
     989            0 :       } else if (equal_ustring(list[0], "set")) {
     990            0 :          xml += "<Set " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">" + list[2] + "</Set>\n";
     991              : 
     992            0 :       } else if (equal_ustring(list[0], "subroutine")) {
     993            0 :          xml += "\n<Subroutine " + qtoString(fullFilename, line + 1, level) + " name=" + q(list[1]) + ">\n";
     994              : 
     995            0 :       } else if (equal_ustring(list[0], "endsubroutine")) {
     996            0 :          xml += "</Subroutine>\n";
     997              : 
     998            0 :       } else if (equal_ustring(list[0], "transition")) {
     999            0 :          xml += "<Transition " + qtoString(fullFilename, line + 1, level) + ">" + list[1] + "</Transition>\n";
    1000              : 
    1001            0 :       } else if (equal_ustring(list[0], "wait")) {
    1002            0 :          if (!list[2][0]) {
    1003            0 :             xml += "<Wait " + qtoString(fullFilename, line + 1, level) + " for=\"seconds\">" + list[1] + "</Wait>\n";
    1004            0 :          } else if (!list[3][0]) {
    1005            0 :             xml += "<Wait " + qtoString(fullFilename, line + 1, level) + " for=" + q(list[1]) + ">" + list[2] + "</Wait>\n";
    1006              :          } else {
    1007            0 :             xml += "<Wait " + qtoString(fullFilename, line + 1, level) + " for=" + q(list[1]) + " path=" + q(list[2]) + " op=" +
    1008            0 :                    q(list[3]) + ">" + list[4] + "</Wait>\n";
    1009              :          }
    1010              : 
    1011            0 :       } else if (list[0][0] == 0 || list[0][0] == '#') {
    1012              :          /* skip empty or out-commented lines */
    1013              :       } else {
    1014            0 :          snprintf(error, error_size, "Invalid command \"%s\"", list[0]);
    1015            0 :          *error_line = line + 1;
    1016            0 :          return FALSE;
    1017              :       }
    1018              :    }
    1019              : 
    1020            0 :    free(lines);
    1021            0 :    free(buf);
    1022            0 :    if (library) {
    1023            0 :       xml += "\n</Library>\n";
    1024              :    } else {
    1025            0 :       xml += "</RunSequence>\n";
    1026              :    }
    1027              : 
    1028              :    // write XML to .xml file
    1029            0 :    fprintf(fout, "%s", xml.c_str());
    1030            0 :    fclose(fout);
    1031              : 
    1032            0 :    return TRUE;
    1033            0 : }
    1034              : 
    1035              : /*------------------------------------------------------------------*/
    1036              : 
    1037            0 : void seq_read(SEQUENCER *seq, SeqCon* c) {
    1038              :    int status;
    1039              :    HNDLE hKey;
    1040              : 
    1041            0 :    status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
    1042            0 :    if (status != DB_SUCCESS) {
    1043            0 :       cm_msg(MERROR, "seq_read", "Cannot find /Sequencer/State in ODB, db_find_key() status %d", status);
    1044            0 :       return;
    1045              :    }
    1046              : 
    1047            0 :    int size = sizeof(SEQUENCER);
    1048            0 :    status = db_get_record1(c->hDB, hKey, seq, &size, 0, strcomb1(sequencer_str).c_str());
    1049            0 :    if (status != DB_SUCCESS) {
    1050            0 :       cm_msg(MERROR, "seq_read", "Cannot get /Sequencer/State from ODB, db_get_record1() status %d", status);
    1051            0 :       return;
    1052              :    }
    1053              : }
    1054              : 
    1055            0 : void seq_write(const SEQUENCER &seq, SeqCon* c) {
    1056              :    int status;
    1057              :    HNDLE hKey;
    1058              : 
    1059            0 :    status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
    1060            0 :    if (status != DB_SUCCESS) {
    1061            0 :       cm_msg(MERROR, "seq_write", "Cannot find /Sequencer/State in ODB, db_find_key() status %d", status);
    1062            0 :       return;
    1063              :    }
    1064            0 :    status = db_set_record(c->hDB, hKey, (void *) &seq, sizeof(SEQUENCER), 0);
    1065            0 :    if (status != DB_SUCCESS) {
    1066            0 :       cm_msg(MERROR, "seq_write", "Cannot write to ODB /Sequencer/State, db_set_record() status %d", status);
    1067            0 :       return;
    1068              :    }
    1069              : }
    1070              : 
    1071              : /*------------------------------------------------------------------*/
    1072              : 
    1073            0 : void seq_clear(SEQUENCER &seq) {
    1074            0 :    seq.running = FALSE;
    1075            0 :    seq.finished = FALSE;
    1076            0 :    seq.paused = FALSE;
    1077            0 :    seq.transition_request = FALSE;
    1078            0 :    seq.wait_limit = 0;
    1079            0 :    seq.wait_value = 0;
    1080            0 :    seq.start_time = 0;
    1081            0 :    seq.wait_type[0] = 0;
    1082            0 :    seq.wait_odb[0] = 0;
    1083            0 :    for (int i = 0; i < SEQ_NEST_LEVEL_LOOP; i++) {
    1084            0 :       seq.loop_start_line[i] = 0;
    1085            0 :       seq.sloop_start_line[i] = 0;
    1086            0 :       seq.loop_end_line[i] = 0;
    1087            0 :       seq.sloop_end_line[i] = 0;
    1088            0 :       seq.loop_counter[i] = 0;
    1089            0 :       seq.loop_n[i] = 0;
    1090              :    }
    1091            0 :    for (int i = 0; i < SEQ_NEST_LEVEL_IF; i++) {
    1092            0 :       seq.if_else_line[i] = 0;
    1093            0 :       seq.if_endif_line[i] = 0;
    1094              :    }
    1095            0 :    for (int i = 0; i < SEQ_NEST_LEVEL_SUB; i++) {
    1096            0 :       seq.subroutine_end_line[i] = 0;
    1097            0 :       seq.subroutine_return_line[i] = 0;
    1098            0 :       seq.subroutine_call_line[i] = 0;
    1099            0 :       seq.ssubroutine_call_line[i] = 0;
    1100            0 :       seq.subroutine_param[i][0] = 0;
    1101              :    }
    1102            0 :    seq.current_line_number = 0;
    1103            0 :    seq.scurrent_line_number = 0;
    1104            0 :    seq.sfilename[0] = 0;
    1105            0 :    seq.if_index = 0;
    1106            0 :    seq.stack_index = 0;
    1107            0 :    seq.error[0] = 0;
    1108            0 :    seq.error_line = 0;
    1109            0 :    seq.serror_line = 0;
    1110            0 :    seq.subdir[0] = 0;
    1111            0 :    seq.subdir_end_line = 0;
    1112            0 :    seq.subdir_not_notify = 0;
    1113            0 :    seq.message[0] = 0;
    1114            0 :    seq.message_wait = FALSE;
    1115            0 :    seq.stop_after_run = FALSE;
    1116            0 : }
    1117              : 
    1118              : /*------------------------------------------------------------------*/
    1119              : 
    1120            0 : static void seq_start(SEQUENCER& seq, SeqCon* c, bool debug) {
    1121            0 :    seq_read(&seq, c);
    1122            0 :    seq_clear(seq);
    1123              : 
    1124              :    // manage sequencer parameters and variables
    1125              : 
    1126            0 :    midas::odb defaults(std::string("/") + c->odb_path + "/Param/Defaults");
    1127            0 :    midas::odb param(std::string("/") + c->odb_path + "/Param/Value");
    1128            0 :    midas::odb vars(std::string("/") + c->odb_path + "/Variables");
    1129              : 
    1130              :    // check if /Param/Value is there
    1131            0 :    for (midas::odb& d: defaults)
    1132            0 :       if (!param.is_subkey(d.get_name())) {
    1133            0 :          mstrlcpy(seq.error, "Cannot start script because /Sequencer/Param/Value is incomplete", sizeof(seq.error));
    1134            0 :          seq_write(seq, c);
    1135            0 :          cm_msg(MERROR, "sequencer", "Cannot start script because /Sequencer/Param/Value is incomplete");
    1136            0 :          return;
    1137              :       }
    1138              : 
    1139              :    // copy /Sequencer/Param/Value to /Sequencer/Variables
    1140            0 :    for (midas::odb& p: param)
    1141            0 :       vars[p.get_name()] = p.s();
    1142              : 
    1143            0 :    if (!c->pnseq) {
    1144            0 :       mstrlcpy(seq.error, "Cannot start script, no script loaded", sizeof(seq.error));
    1145            0 :       seq_write(seq, c);
    1146            0 :       return;
    1147              :    }
    1148              : 
    1149              :    // start sequencer
    1150            0 :    seq.running = TRUE;
    1151            0 :    seq.debug = debug;
    1152            0 :    seq.current_line_number = 1;
    1153            0 :    seq.scurrent_line_number = 1;
    1154            0 :    mstrlcpy(seq.sfilename, seq.filename, sizeof(seq.sfilename));
    1155            0 :    seq_write(seq, c);
    1156              : 
    1157            0 :    if (debug)
    1158            0 :       cm_msg(MTALK, "sequencer", "Sequencer started with script \"%s\" in debugging mode.", seq.filename);
    1159              :    else
    1160            0 :       cm_msg(MTALK, "sequencer", "Sequencer started with script \"%s\".", seq.filename);
    1161            0 : }
    1162              : 
    1163              : /*------------------------------------------------------------------*/
    1164              : 
    1165            0 : static void seq_stop(SEQUENCER& seq, SeqCon* c) {
    1166            0 :    seq_read(&seq, c);
    1167              : 
    1168            0 :    if (seq.follow_libraries) {
    1169            0 :       std::string path(cm_get_path());
    1170            0 :       path += "userfiles/sequencer/";
    1171            0 :       path += seq.path;
    1172            0 :       path += seq.filename;
    1173              : 
    1174            0 :       loadMSL(c->odbs, path);
    1175            0 :    }
    1176              : 
    1177            0 :    seq_clear(seq);
    1178            0 :    seq.finished = TRUE;
    1179            0 :    seq_write(seq, c);
    1180            0 : }
    1181              : 
    1182              : /*------------------------------------------------------------------*/
    1183              : 
    1184            0 : static void seq_stop_after_run(SEQUENCER& seq, SeqCon* c) {
    1185            0 :    seq_read(&seq, c);
    1186            0 :    seq.stop_after_run = true;
    1187            0 :    seq_write(seq, c);
    1188            0 : }
    1189              : 
    1190              : /*------------------------------------------------------------------*/
    1191              : 
    1192            0 : static void seq_cancel_stop_after_run(SEQUENCER& seq, SeqCon* c) {
    1193            0 :    seq_read(&seq, c);
    1194            0 :    seq.stop_after_run = false;
    1195            0 :    seq_write(seq, c);
    1196            0 : }
    1197              : 
    1198              : /*------------------------------------------------------------------*/
    1199              : 
    1200            0 : static void seq_pause(SEQUENCER& seq, SeqCon* c, bool flag) {
    1201            0 :    seq_read(&seq, c);
    1202            0 :    seq.paused = flag;
    1203            0 :    if (!flag)
    1204            0 :       seq.debug = false;
    1205            0 :    seq_write(seq, c);
    1206            0 : }
    1207              : 
    1208              : /*------------------------------------------------------------------*/
    1209              : 
    1210            0 : static BOOL goto_at_exit(SEQUENCER& seq, SeqCon* c) {
    1211            0 :    seq_read(&seq, c);
    1212              : 
    1213              :    // search ATEXIT subroutine
    1214            0 :    for (int i = 1; i < mxml_get_line_number_end(mxml_find_node(c->pnseq, "RunSequence")); i++) {
    1215            0 :       PMXML_NODE pt = mxml_get_node_at_line(c->pnseq, i);
    1216            0 :       if (pt) {
    1217            0 :          if (equal_ustring(mxml_get_name(pt), "Subroutine")) {
    1218            0 :             if (equal_ustring(mxml_get_attribute(pt, "name"), "ATEXIT")) {
    1219              : 
    1220            0 :                if (seq.stack_index == SEQ_NEST_LEVEL_SUB) {
    1221            0 :                   seq_error(seq, c, "Maximum subroutine level exceeded");
    1222            0 :                   return FALSE;
    1223              :                }
    1224              : 
    1225              :                // put routine end line on end stack
    1226            0 :                seq.subroutine_end_line[seq.stack_index] = mxml_get_line_number_end(pt);
    1227            0 :                seq.ssubroutine_call_line[seq.stack_index] = atoi(mxml_get_attribute(pt, "l"));
    1228            0 :                seq.subroutine_return_line[seq.stack_index] = -1; // indicates exit on return
    1229              : 
    1230              :                // put routine end line on end stack
    1231            0 :                seq.subroutine_end_line[seq.stack_index] = mxml_get_line_number_end(pt);
    1232              : 
    1233              :                // go to first line of subroutine
    1234            0 :                seq.current_line_number = mxml_get_line_number_start(pt) + 1;
    1235              : 
    1236              :                // increment stack
    1237            0 :                seq.stack_index++;
    1238              : 
    1239            0 :                seq_write(seq, c);
    1240              : 
    1241            0 :                return TRUE;
    1242              :             }
    1243              :          }
    1244              :       }
    1245              :    }
    1246              : 
    1247              :    // look for atexit subroutine
    1248            0 :    return FALSE;
    1249              : }
    1250              : 
    1251              : /*------------------------------------------------------------------*/
    1252              : 
    1253            0 : static void seq_step_over(SEQUENCER& seq, SeqCon* c) {
    1254            0 :    seq_read(&seq, c);
    1255            0 :    seq.paused = false;
    1256            0 :    seq.debug = true;
    1257            0 :    seq_write(seq, c);
    1258            0 : }
    1259              : 
    1260              : /*------------------------------------------------------------------*/
    1261              : 
    1262            0 : static void seq_open_file(const char *str, SEQUENCER &seq, SeqCon* c) {
    1263            0 :    seq.new_file = FALSE;
    1264            0 :    seq.error[0] = 0;
    1265            0 :    seq.error_line = 0;
    1266            0 :    seq.serror_line = 0;
    1267            0 :    if (c->pnseq) {
    1268            0 :       mxml_free_tree(c->pnseq);
    1269            0 :       c->pnseq = NULL;
    1270              :    }
    1271            0 :    c->odbs->WS("Script/Lines", "");
    1272              : 
    1273            0 :    if (stristr(str, ".msl")) {
    1274            0 :       int size = (int)strlen(str) + 1;
    1275            0 :       char *xml_filename = (char *) malloc(size);
    1276            0 :       mstrlcpy(xml_filename, str, size);
    1277            0 :       strsubst(xml_filename, size, ".msl", ".xml");
    1278              :       //printf("Parsing MSL sequencer file: %s to XML sequencer file %s\n", str, xml_filename);
    1279              : 
    1280            0 :       std::string path(cm_get_path());
    1281            0 :       path += "userfiles/sequencer/";
    1282              : 
    1283            0 :       if (xml_filename[0] == '/') {
    1284            0 :          path += xml_filename;
    1285            0 :          path = path.substr(0, path.find_last_of('/') + 1);
    1286            0 :          mstrlcpy(xml_filename, path.substr(path.find_last_of('/') + 1).c_str(), size);
    1287              :       }  else {
    1288            0 :          path += seq.path;
    1289              :       }
    1290              : 
    1291            0 :       if (msl_parse(seq, c, 0, path.c_str(), str, xml_filename, seq.error, sizeof(seq.error), &seq.serror_line)) {
    1292              :          //printf("Loading XML sequencer file: %s\n", xml_filename);
    1293            0 :          std::string fn(path);
    1294            0 :          if (!fn.empty() && fn.back() != '/')
    1295            0 :             fn += '/';
    1296            0 :          fn += xml_filename;
    1297            0 :          c->pnseq = mxml_parse_file(fn.c_str(), seq.error, sizeof(seq.error), &seq.error_line);
    1298            0 :       } else {
    1299              :          //printf("Error in MSL sequencer file \"%s\" line %d, error: %s\n", str, seq.serror_line, seq.error);
    1300              :       }
    1301            0 :       free(xml_filename);
    1302            0 :    } else {
    1303              :       //printf("Loading XML sequencer file: %s\n", str);
    1304            0 :       c->pnseq = mxml_parse_file(str, seq.error, sizeof(seq.error), &seq.error_line);
    1305              :    }
    1306            0 : }
    1307              : 
    1308              : /*------------------------------------------------------------------*/
    1309              : 
    1310            0 : static void seq_start_next(SEQUENCER &seq, SeqCon* c) {
    1311            0 :    seq_read(&seq, c);
    1312            0 :    if (seq.next_filename[0][0]) {
    1313            0 :       mstrlcpy(seq.filename, seq.next_filename[0], sizeof(seq.filename));
    1314            0 :       for (int i=0 ; i<9 ; i++)
    1315            0 :          mstrlcpy(seq.next_filename[i], seq.next_filename[i+1], 256);
    1316            0 :       seq.next_filename[9][0] = 0;
    1317            0 :       seq_write(seq, c);
    1318              : 
    1319            0 :       seq_open_file(seq.filename, seq, c);
    1320              : 
    1321            0 :       seq_start(seq, c, false);
    1322              :    }
    1323            0 : }
    1324              : 
    1325              : /*------------------------------------------------------------------*/
    1326              : 
    1327            0 : static void seq_watch(HNDLE hDB, HNDLE hKeyChanged, int index, void *info) {
    1328              :    char str[256];
    1329            0 :    SeqCon* c = (SeqCon*)info;
    1330              : 
    1331            0 :    seq_read(c->seqp, c);
    1332              : 
    1333            0 :    if (c->seqp->new_file) {
    1334            0 :       mstrlcpy(str, c->seqp->path, sizeof(str));
    1335            0 :       if (strlen(str) > 1 && str[strlen(str) - 1] != DIR_SEPARATOR)
    1336            0 :          mstrlcat(str, DIR_SEPARATOR_STR, sizeof(str));
    1337            0 :       mstrlcat(str, c->seqp->filename, sizeof(str));
    1338              : 
    1339              :       //printf("Load file %s\n", str);
    1340              : 
    1341            0 :       seq_open_file(str, *(c->seqp), c);
    1342              : 
    1343            0 :       seq_clear(*(c->seqp));
    1344              : 
    1345            0 :       seq_write(*(c->seqp), c);
    1346              :    }
    1347            0 : }
    1348              : 
    1349              : /*------------------------------------------------------------------*/
    1350              : 
    1351            0 : static void seq_watch_command(HNDLE hDB, HNDLE hKeyChanged, int index, void *info) {
    1352            0 :    SeqCon* c = (SeqCon*)info;
    1353            0 :    SEQUENCER* seqp = c->seqp;
    1354              : 
    1355            0 :    bool start_script = false;
    1356            0 :    bool stop_immediately = false;
    1357            0 :    bool stop_after_run = false;
    1358            0 :    bool cancel_stop_after_run = false;
    1359            0 :    bool pause_script = false;
    1360            0 :    bool resume_script = false;
    1361            0 :    bool debug_script = false;
    1362            0 :    bool step_over = false;
    1363            0 :    bool load_new_file = false;
    1364              : 
    1365            0 :    c->odbs->RB("Command/Start script", &start_script);
    1366            0 :    c->odbs->RB("Command/Stop immediately", &stop_immediately);
    1367            0 :    c->odbs->RB("Command/Stop after run", &stop_after_run);
    1368            0 :    c->odbs->RB("Command/Cancel stop after run", &cancel_stop_after_run);
    1369            0 :    c->odbs->RB("Command/Pause script", &pause_script);
    1370            0 :    c->odbs->RB("Command/Resume script", &resume_script);
    1371            0 :    c->odbs->RB("Command/Debug script", &debug_script);
    1372            0 :    c->odbs->RB("Command/Step over", &step_over);
    1373            0 :    c->odbs->RB("Command/Load new file", &load_new_file);
    1374              : 
    1375            0 :    if (load_new_file) {
    1376            0 :       std::string filename;
    1377            0 :       c->odbs->RS("State/Filename", &filename);
    1378              : 
    1379              :       // printf("## load new file %s\n", filename.c_str());
    1380              : 
    1381            0 :       if (filename.find("..") != std::string::npos) {
    1382            0 :          mstrlcpy(seqp->error, "Cannot load \"", sizeof(seqp->error));
    1383            0 :          mstrlcat(seqp->error, filename.c_str(), sizeof(seqp->error));
    1384            0 :          mstrlcat(seqp->error, "\": file names with \"..\" is not permitted", sizeof(seqp->error));
    1385            0 :          seq_write(*seqp, c);
    1386            0 :       } else if (filename.find(".msl") == std::string::npos) {
    1387            0 :          mstrlcpy(seqp->error, "Cannot load \"", sizeof(seqp->error));
    1388            0 :          mstrlcat(seqp->error, filename.c_str(), sizeof(seqp->error));
    1389            0 :          mstrlcat(seqp->error, "\": file name should end with \".msl\"", sizeof(seqp->error));
    1390            0 :          seq_write(*seqp, c);
    1391              :       } else {
    1392            0 :          mstrlcpy(seqp->filename, filename.c_str(), sizeof(seqp->filename));
    1393            0 :          std::string path = cm_expand_env(seqp->path);
    1394            0 :          if (path.length() > 0 && path.back() != '/') {
    1395            0 :             path += "/";
    1396              :          }
    1397              :          HNDLE hDB;
    1398            0 :          cm_get_experiment_database(&hDB, NULL);
    1399            0 :          seq_clear(*seqp);
    1400            0 :          seq_open_file(filename.c_str(), *seqp, c);
    1401            0 :          seq_write(*seqp, c);
    1402            0 :       }
    1403              : 
    1404              :       // indicate that we successfully load the new file
    1405            0 :       c->odbs->WB("Command/Load new file", false);
    1406              : 
    1407              :       // printf("## Set 'load new file' false\n");
    1408            0 :    }
    1409              : 
    1410            0 :    if (start_script) {
    1411            0 :       c->odbs->WB("Command/Start script", false);
    1412              : 
    1413            0 :       bool seq_running = false;
    1414            0 :       c->odbs->RB("State/running", &seq_running);
    1415              : 
    1416            0 :       if (!seq_running) {
    1417            0 :          seq_start(*seqp, c, false);
    1418              :       } else {
    1419            0 :          cm_msg(MTALK, "sequencer", "Sequencer is already running");
    1420              :       }
    1421              :    }
    1422              : 
    1423            0 :    if (stop_immediately) {
    1424            0 :       if (!goto_at_exit(*seqp, c)) {
    1425            0 :          c->odbs->WB("Command/Stop immediately", false);
    1426            0 :          seq_stop(*seqp, c);
    1427            0 :          cm_msg(MTALK, "sequencer", "Sequencer is finished by \"stop immediately\".");
    1428              :       } else {
    1429            0 :          c->odbs->WB("Command/Stop immediately", false);
    1430            0 :          cm_msg(MTALK, "sequencer", "Sequencer received \"stop immediately\". Executing ATEXIT.");
    1431              :       }
    1432              :    }
    1433              : 
    1434            0 :    if (stop_after_run) {
    1435            0 :       c->odbs->WB("Command/Stop after run", false);
    1436            0 :       seq_stop_after_run(*seqp, c);
    1437              :    }
    1438              : 
    1439            0 :    if (cancel_stop_after_run) {
    1440            0 :       c->odbs->WB("Command/Cancel stop after run", false);
    1441            0 :       seq_cancel_stop_after_run(*seqp, c);
    1442              :    }
    1443              : 
    1444            0 :    if (pause_script) {
    1445            0 :       seq_pause(*seqp, c, true);
    1446            0 :       c->odbs->WB("Command/Pause script", false);
    1447            0 :       cm_msg(MTALK, "sequencer", "Sequencer is paused.");
    1448              :    }
    1449              : 
    1450            0 :    if (resume_script) {
    1451            0 :       seq_pause(*seqp, c, false);
    1452            0 :       c->odbs->WB("Command/Resume script", false);
    1453            0 :       cm_msg(MTALK, "sequencer", "Sequencer is resumed.");
    1454              :    }
    1455              : 
    1456            0 :    if (debug_script) {
    1457            0 :       c->odbs->WB("Command/Debug script", false);
    1458              : 
    1459            0 :       bool seq_running = false;
    1460            0 :       c->odbs->RB("State/running", &seq_running);
    1461              : 
    1462            0 :       if (!seq_running) {
    1463            0 :          seq_start(*seqp, c, true);
    1464              :       } else {
    1465            0 :          cm_msg(MTALK, "sequencer", "Sequencer is already running");
    1466              :       }
    1467              :    }
    1468              : 
    1469            0 :    if (step_over) {
    1470            0 :       seq_step_over(*seqp, c);
    1471            0 :       c->odbs->WB("Command/Step over", false);
    1472              :    }
    1473            0 : }
    1474              : 
    1475              : /*------------------------------------------------------------------*/
    1476              : 
    1477              : //performs array index extraction including sequencer variables
    1478            0 : void seq_array_index(SEQUENCER& seq, SeqCon* c, char *odbpath, int *index1, int *index2) {
    1479              :    char str[256];
    1480            0 :    *index1 = *index2 = 0;
    1481            0 :    if (odbpath[strlen(odbpath) - 1] == ']') {
    1482            0 :       if (strchr(odbpath, '[')) {
    1483              :          //check for sequencer variables
    1484            0 :          if (strchr((strchr(odbpath, '[') + 1), '$')) {
    1485            0 :             mstrlcpy(str, strchr(odbpath, '[') + 1, sizeof(str));
    1486            0 :             if (strchr(str, ']'))
    1487            0 :                *strchr(str, ']') = 0;
    1488            0 :             *index1 = atoi(eval_var(seq, c, str).c_str());
    1489              : 
    1490            0 :             *strchr(odbpath, '[') = 0;
    1491              :          } else {
    1492              :             //standard expansion
    1493            0 :             strarrayindex(odbpath, index1, index2);
    1494              :          }
    1495              :       }
    1496              :    }
    1497            0 : }
    1498              : 
    1499              : /*------------------------------------------------------------------*/
    1500              : 
    1501              : //set all matching keys to a value
    1502            0 : int set_all_matching(HNDLE hDB, HNDLE hBaseKey, char *odbpath, char *value, int index1, int index2, int notify) {
    1503              :    int status, size;
    1504              :    char data[256];
    1505              :    KEY key;
    1506              : 
    1507            0 :    std::vector<HNDLE> keys;
    1508            0 :    status = db_find_keys(hDB, hBaseKey, odbpath, keys);
    1509              : 
    1510            0 :    if (status != DB_SUCCESS)
    1511            0 :       return status;
    1512              : 
    1513            0 :    for (HNDLE hKey: keys) {
    1514            0 :       db_get_key(hDB, hKey, &key);
    1515            0 :       size = sizeof(data);
    1516            0 :       db_sscanf(value, data, &size, 0, key.type);
    1517              : 
    1518              :       /* extend data size for single string if necessary */
    1519            0 :       if ((key.type == TID_STRING || key.type == TID_LINK)
    1520            0 :           && (int) strlen(data) + 1 > key.item_size && key.num_values == 1)
    1521            0 :          key.item_size = strlen(data) + 1;
    1522              : 
    1523            0 :       if (key.num_values > 1 && index1 == -1) {
    1524            0 :          for (int i = 0; i < key.num_values; i++)
    1525            0 :             status = db_set_data_index1(hDB, hKey, data, key.item_size, i, key.type, notify);
    1526            0 :       } else if (key.num_values > 1 && index2 > index1) {
    1527            0 :          if (index1 < 0 || index1 >= key.num_values || index2 < 0 || index2 >= key.num_values)
    1528            0 :             return DB_INVALID_PARAM;
    1529            0 :          for (int i = index1; i < key.num_values && i <= index2; i++)
    1530            0 :             status = db_set_data_index1(hDB, hKey, data, key.item_size, i, key.type, notify);
    1531            0 :       } else {
    1532            0 :          if (index1 < 0 || index1 >= key.num_values)
    1533            0 :             return DB_INVALID_PARAM;
    1534            0 :          status = db_set_data_index1(hDB, hKey, data, key.item_size, index1, key.type, notify);
    1535              :       }
    1536              : 
    1537            0 :       if (status != DB_SUCCESS) {
    1538            0 :          return status;
    1539              :       }
    1540              :    }
    1541              : 
    1542            0 :    return DB_SUCCESS;
    1543            0 : }
    1544              : 
    1545              : /*------------------------------------------------------------------*/
    1546              : 
    1547            0 : void sequencer(SEQUENCER& seq, SeqCon* c) {
    1548              :    PMXML_NODE pn, pr, pt, pe;
    1549              :    char odbpath[256], data[256], str[1024], name[32], op[32];
    1550            0 :    std::string value;
    1551              :    char list[100][XNAME_LENGTH];
    1552              :    int i, j, l, n, status, size, index1, index2, state, run_number, cont;
    1553              :    HNDLE hKey, hKeySeq;
    1554              :    KEY key;
    1555              :    double d;
    1556            0 :    BOOL skip_step = FALSE;
    1557              : 
    1558            0 :    if (!seq.running || seq.paused) {
    1559            0 :       ss_sleep(10);
    1560            0 :       return;
    1561              :    }
    1562              : 
    1563            0 :    if (c->pnseq == NULL) {
    1564            0 :       seq_stop(seq, c);
    1565            0 :       mstrlcpy(seq.error, "No script loaded", sizeof(seq.error));
    1566            0 :       seq_write(seq, c);
    1567            0 :       ss_sleep(10);
    1568            0 :       return;
    1569              :    }
    1570              : 
    1571            0 :    db_find_key(c->hDB, c->hSeq, "State", &hKeySeq);
    1572            0 :    if (!hKeySeq)
    1573            0 :       return;
    1574              : 
    1575              :    // Retrieve last midas message and put it into seq structure (later sent to ODB via db_set_record()
    1576            0 :    cm_msg_retrieve(1, str, sizeof(str));
    1577            0 :    str[19] = 0;
    1578            0 :    strcpy(seq.last_msg, str+11);
    1579              : 
    1580            0 :    pr = mxml_find_node(c->pnseq, "RunSequence");
    1581            0 :    if (!pr) {
    1582            0 :       seq_error(seq, c, "Cannot find &lt;RunSequence&gt; tag in XML file");
    1583            0 :       return;
    1584              :    }
    1585              : 
    1586            0 :    int last_line = mxml_get_line_number_end(pr);
    1587              : 
    1588              :    /* check for Subroutine end */
    1589            0 :    if (seq.stack_index > 0 && seq.current_line_number == seq.subroutine_end_line[seq.stack_index - 1]) {
    1590            0 :       seq_read(&seq, c);
    1591            0 :       seq.subroutine_end_line[seq.stack_index - 1] = 0;
    1592              : 
    1593            0 :       if (seq.subroutine_return_line[seq.stack_index - 1] == -1) {
    1594              :          // end of atexit subroutine
    1595            0 :          seq_stop(seq, c);
    1596            0 :          cm_msg(MTALK, "sequencer", "Sequencer is finished.");
    1597              : 
    1598            0 :          seq_start_next(seq, c);
    1599            0 :          return;
    1600              :       }
    1601              : 
    1602            0 :       seq.current_line_number = seq.subroutine_return_line[seq.stack_index - 1];
    1603            0 :       seq.subroutine_return_line[seq.stack_index - 1] = 0;
    1604            0 :       seq.subroutine_call_line[seq.stack_index - 1] = 0;
    1605            0 :       seq.ssubroutine_call_line[seq.stack_index - 1] = 0;
    1606            0 :       seq.stack_index--;
    1607            0 :       seq_write(seq, c);
    1608            0 :       return;
    1609              :    }
    1610              : 
    1611              :    /* check for last line of script */
    1612            0 :    if (seq.current_line_number > last_line) {
    1613              : 
    1614            0 :       if (!goto_at_exit(seq, c)) {
    1615            0 :          seq_stop(seq, c);
    1616            0 :          cm_msg(MTALK, "sequencer", "Sequencer is finished.");
    1617              : 
    1618            0 :          seq_start_next(seq, c);
    1619              :       }
    1620            0 :       return;
    1621              :    }
    1622              : 
    1623              :    /* check for loop end */
    1624            0 :    for (i = SEQ_NEST_LEVEL_LOOP-1; i >= 0; i--)
    1625            0 :       if (seq.loop_start_line[i] > 0)
    1626            0 :          break;
    1627            0 :    if (i >= 0) {
    1628            0 :       if (seq.current_line_number == seq.loop_end_line[i]) {
    1629            0 :          size = sizeof(seq);
    1630            0 :          db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
    1631              : 
    1632            0 :          if (seq.loop_counter[i] == seq.loop_n[i]) {
    1633            0 :             seq.loop_counter[i] = 0;
    1634            0 :             seq.loop_start_line[i] = 0;
    1635            0 :             seq.sloop_start_line[i] = 0;
    1636            0 :             seq.loop_end_line[i] = 0;
    1637            0 :             seq.sloop_end_line[i] = 0;
    1638            0 :             seq.loop_n[i] = 0;
    1639            0 :             seq.current_line_number++;
    1640              :          } else {
    1641            0 :             pn = mxml_get_node_at_line(c->pnseq, seq.loop_start_line[i]);
    1642            0 :             if (mxml_get_attribute(pn, "var")) {
    1643            0 :                mstrlcpy(name, mxml_get_attribute(pn, "var"), sizeof(name));
    1644            0 :                if (mxml_get_attribute(pn, "values")) {
    1645            0 :                   mstrlcpy(data, mxml_get_attribute(pn, "values"), sizeof(data));
    1646            0 :                   strbreak(data, list, 100, ",", FALSE);
    1647            0 :                   value = eval_var(seq, c, list[seq.loop_counter[i]]);
    1648            0 :                } else if (mxml_get_attribute(pn, "n")) {
    1649            0 :                   value = std::to_string(seq.loop_counter[i] + 1);
    1650              :                }
    1651            0 :                sprintf(str, "Variables/%s", name); // FIXME: unsafe
    1652            0 :                size = value.length() + 1;
    1653            0 :                if (size < 32)
    1654            0 :                   size = 32;
    1655            0 :                db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
    1656              :             }
    1657            0 :             seq.loop_counter[i]++;
    1658            0 :             seq.current_line_number = seq.loop_start_line[i] + 1;
    1659              :          }
    1660            0 :          db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
    1661            0 :          return;
    1662              :       }
    1663              :    }
    1664              : 
    1665              :    /* check for end of "if" statement */
    1666            0 :    if (seq.if_index > 0 && seq.current_line_number == seq.if_endif_line[seq.if_index - 1]) {
    1667            0 :       size = sizeof(seq);
    1668            0 :       db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
    1669            0 :       seq.if_index--;
    1670            0 :       seq.if_line[seq.if_index] = 0;
    1671            0 :       seq.if_else_line[seq.if_index] = 0;
    1672            0 :       seq.if_endif_line[seq.if_index] = 0;
    1673            0 :       seq.current_line_number++;
    1674            0 :       db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
    1675            0 :       return;
    1676              :    }
    1677              : 
    1678              :    /* check for ODBSubdir end */
    1679            0 :    if (seq.current_line_number == seq.subdir_end_line) {
    1680            0 :       size = sizeof(seq);
    1681            0 :       db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
    1682            0 :       seq.subdir_end_line = 0;
    1683            0 :       seq.subdir[0] = 0;
    1684            0 :       seq.subdir_not_notify = FALSE;
    1685            0 :       db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
    1686            0 :       return;
    1687              :    }
    1688              : 
    1689              :    /* find node belonging to current line */
    1690            0 :    pn = mxml_get_node_at_line(c->pnseq, seq.current_line_number);
    1691            0 :    if (!pn) {
    1692            0 :       size = sizeof(seq);
    1693            0 :       db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
    1694            0 :       seq.current_line_number++;
    1695            0 :       db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
    1696            0 :       return;
    1697              :    }
    1698              : 
    1699              :    /* set MSL line from current element and put script into ODB if changed (library call) */
    1700            0 :    pn = mxml_get_node_at_line(c->pnseq, seq.current_line_number);
    1701            0 :    if (pn) {
    1702            0 :       if (seq.follow_libraries) {
    1703            0 :          if (mxml_get_attribute(pn, "l"))
    1704            0 :             seq.scurrent_line_number = atoi(mxml_get_attribute(pn, "l"));
    1705            0 :          if (mxml_get_attribute(pn, "fn")) {
    1706            0 :             std::string filename = mxml_get_attribute(pn, "fn");
    1707              : 
    1708              :             // load file into ODB if changed
    1709            0 :             if (filename != std::string(seq.sfilename)) {
    1710            0 :                if (loadMSL(c->odbs, filename))
    1711            0 :                   mstrlcpy(seq.sfilename, filename.c_str(), sizeof(seq.sfilename));
    1712              :             }
    1713            0 :          }
    1714              :       } else {
    1715            0 :          if (mxml_get_attribute(pn, "l") && (!mxml_get_attribute(pn, "lvl") || atoi(mxml_get_attribute(pn, "lvl")) == 0))
    1716            0 :             seq.scurrent_line_number = atoi(mxml_get_attribute(pn, "l"));
    1717              :       }
    1718              :    }
    1719              : 
    1720              : 
    1721              :    // out-comment following lines for debug output
    1722              : #if 0
    1723              :    if (seq.scurrent_line_number >= 0) {
    1724              :       midas::odb o("/" + c->odb_path + "/Script/Lines");
    1725              :       std::string s = o[seq.scurrent_line_number - 1];
    1726              :       printf("%3d: %s\n", seq.scurrent_line_number, s.c_str());
    1727              :    }
    1728              : #endif
    1729              : 
    1730            0 :    if (equal_ustring(mxml_get_name(pn), "PI") || equal_ustring(mxml_get_name(pn), "RunSequence") ||
    1731            0 :        equal_ustring(mxml_get_name(pn), "Comment")) {
    1732              :       // just skip
    1733            0 :       seq.current_line_number++;
    1734            0 :       skip_step = TRUE;
    1735              :    }
    1736              : 
    1737              :    /*---- ODBSubdir ----*/
    1738            0 :    else if (equal_ustring(mxml_get_name(pn), "ODBSubdir")) {
    1739            0 :       if (!mxml_get_attribute(pn, "path")) {
    1740            0 :          seq_error(seq, c, "Missing attribute \"path\"");
    1741              :       } else {
    1742              : 
    1743            0 :          std::string s = mxml_get_attribute(pn, "path");
    1744            0 :          if (s.find('$') != std::string::npos)
    1745            0 :             s = eval_var(seq, c, s);
    1746              : 
    1747            0 :          mstrlcpy(seq.subdir, s.c_str(), sizeof(seq.subdir));
    1748            0 :          if (mxml_get_attribute(pn, "notify"))
    1749            0 :             seq.subdir_not_notify = !atoi(mxml_get_attribute(pn, "notify"));
    1750            0 :          seq.subdir_end_line = mxml_get_line_number_end(pn);
    1751            0 :          seq.current_line_number++;
    1752            0 :       }
    1753              :    }
    1754              : 
    1755              :    /*---- ODBSet ----*/
    1756            0 :    else if (equal_ustring(mxml_get_name(pn), "ODBSet")) {
    1757            0 :       if (!mxml_get_attribute(pn, "path")) {
    1758            0 :          seq_error(seq, c, "Missing attribute \"path\"");
    1759              :       } else {
    1760            0 :          mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
    1761            0 :          if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
    1762            0 :             mstrlcat(odbpath, "/", sizeof(odbpath));
    1763            0 :          mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
    1764              : 
    1765            0 :          if (strchr(odbpath, '$')) {
    1766            0 :             if (strchr(odbpath, '[')) {
    1767              :                // keep $ in index for later evaluation
    1768            0 :                std::string s(odbpath);
    1769            0 :                std::string s1 = s.substr(0, s.find('['));
    1770            0 :                std::string s2 = s.substr(s.find('['));
    1771            0 :                s1 = eval_var(seq, c, s1);
    1772            0 :                s1 += s2;
    1773            0 :                mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
    1774            0 :             } else {
    1775              :                // evaluate variable in path
    1776            0 :                std::string s(odbpath);
    1777            0 :                s = eval_var(seq, c, s);
    1778            0 :                mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
    1779            0 :             }
    1780              :          }
    1781              : 
    1782            0 :          int notify = TRUE;
    1783            0 :          if (seq.subdir_not_notify)
    1784            0 :             notify = FALSE;
    1785            0 :          if (mxml_get_attribute(pn, "notify"))
    1786            0 :             notify = atoi(mxml_get_attribute(pn, "notify"));
    1787              : 
    1788            0 :          index1 = index2 = 0;
    1789            0 :          seq_array_index(seq, c, odbpath, &index1, &index2);
    1790              : 
    1791            0 :          if (index1 < -1 || index2 < 0) {
    1792            0 :             seq_error(seq, c, "Negative index not allowed");
    1793            0 :             return;
    1794              :          }
    1795              : 
    1796            0 :          if (index1 > 1E6 || index2 > 1E6) {
    1797            0 :             seq_error(seq, c, "Index too large for ODB");
    1798            0 :             return;
    1799              :          }
    1800              : 
    1801            0 :          value = eval_var(seq, c, mxml_get_value(pn));
    1802              : 
    1803            0 :          status = set_all_matching(c->hDB, 0, odbpath, (char *)value.c_str(), index1, index2, notify);
    1804              : 
    1805            0 :          if (status == DB_SUCCESS) {
    1806            0 :             size = sizeof(seq);
    1807            0 :             db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
    1808            0 :             seq.current_line_number++;
    1809            0 :          } else if (status == DB_NO_KEY) {
    1810            0 :             sprintf(str, "ODB key \"%s\" not found", odbpath); // FIXME: unsafe
    1811            0 :             seq_error(seq, c, str);
    1812            0 :          } else if (status == DB_INVALID_PARAM) {
    1813            0 :             sprintf(str, "Invalid index %d for ODB key \"%s\"", index1, odbpath); // FIXME: unsafe
    1814            0 :             seq_error(seq, c, str);
    1815              :          } else {
    1816              :             //something went really wrong
    1817            0 :             sprintf(str, "Internal error %d", status);
    1818            0 :             seq_error(seq, c, str);
    1819            0 :             return;
    1820              :          }
    1821              :       }
    1822              :    }
    1823              : 
    1824              :    /*---- ODBLoad ----*/
    1825            0 :    else if (equal_ustring(mxml_get_name(pn), "ODBLoad")) {
    1826            0 :       if (mxml_get_value(pn)[0] == '/') {
    1827              : 
    1828              :          // path relative to the one set in <exp>/userfiles/sequencer
    1829            0 :          std::string path(cm_get_path());
    1830            0 :          path += "userfiles/sequencer/";
    1831            0 :          value = path;
    1832            0 :          value += std::string(mxml_get_value(pn)+1);
    1833            0 :       } else {
    1834              :          // relative path to msl file
    1835            0 :          std::string path(cm_get_path());
    1836            0 :          path += "userfiles/sequencer/";
    1837            0 :          path += seq.path;
    1838            0 :          value = path;
    1839            0 :          if (value.back() != '/')
    1840            0 :             value += "/";
    1841            0 :          value += mxml_get_value(pn);
    1842            0 :       }
    1843              : 
    1844            0 :       if (value.find('$') != std::string::npos)
    1845            0 :          value = eval_var(seq, c, value);
    1846              : 
    1847              :       // if path attribute is given
    1848            0 :       if (mxml_get_attribute(pn, "path")) {
    1849            0 :          mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
    1850            0 :          if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
    1851            0 :             mstrlcat(odbpath, "/", sizeof(odbpath));
    1852            0 :          mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
    1853              : 
    1854            0 :          if (strchr(odbpath, '$')) {
    1855            0 :             std::string s(odbpath);
    1856            0 :             s = eval_var(seq, c, s);
    1857            0 :             mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
    1858            0 :          }
    1859              : 
    1860              :          // load at that key, if exists
    1861            0 :          status = db_find_key(c->hDB, 0, odbpath, &hKey);
    1862            0 :          if (status != DB_SUCCESS) {
    1863            0 :             seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
    1864            0 :             return;
    1865              :          } else {
    1866            0 :             status = db_load(c->hDB, hKey, value.c_str(), FALSE);
    1867              :          }
    1868              :       } else {
    1869              :          // otherwise load at root
    1870            0 :          status = db_load(c->hDB, 0, value.c_str(), FALSE);
    1871              :       }
    1872              : 
    1873            0 :       if (status == DB_SUCCESS) {
    1874            0 :          size = sizeof(seq);
    1875            0 :          db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
    1876            0 :          seq.current_line_number++;
    1877            0 :       } else if (status == DB_FILE_ERROR) {
    1878            0 :          seq_error(seq, c, msprintf("Error reading file \"%s\"", value.c_str()).c_str());
    1879              :       } else {
    1880              :          //something went really wrong
    1881            0 :          seq_error(seq, c, "Internal error loading ODB file!");
    1882            0 :          return;
    1883              :       }
    1884              :    }
    1885              : 
    1886              :    /*---- ODBSave ----*/
    1887            0 :    else if (equal_ustring(mxml_get_name(pn), "ODBSave")) {
    1888            0 :       if (mxml_get_value(pn)[0] == '/') {
    1889              : 
    1890              :          // path relative to the one set in <exp>/userfiles/sequencer
    1891            0 :          std::string path(cm_get_path());
    1892            0 :          path += "userfiles/sequencer/";
    1893            0 :          value = path;
    1894            0 :          value += std::string(mxml_get_value(pn)+1);
    1895            0 :       } else {
    1896              :          // relative path to msl file
    1897            0 :          std::string path(cm_get_path());
    1898            0 :          path += "userfiles/sequencer/";
    1899            0 :          path += seq.path;
    1900            0 :          value = path;
    1901            0 :          value += seq.filename;
    1902            0 :          size_t pos = value.find_last_of('/');
    1903            0 :          if (pos != std::string::npos)
    1904            0 :             value = value.substr(0, pos);
    1905            0 :          value += mxml_get_value(pn);
    1906            0 :       }
    1907              : 
    1908            0 :       if (value.find('$') != std::string::npos)
    1909            0 :          value = eval_var(seq, c, value);
    1910              : 
    1911              :       // if path attribute is given
    1912            0 :       if (mxml_get_attribute(pn, "path") && *mxml_get_attribute(pn, "path")) {
    1913            0 :          mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
    1914            0 :          if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
    1915            0 :             mstrlcat(odbpath, "/", sizeof(odbpath));
    1916            0 :          mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
    1917              : 
    1918            0 :          if (strchr(odbpath, '$')) {
    1919            0 :             std::string s(odbpath);
    1920            0 :             s = eval_var(seq, c, s);
    1921            0 :             mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
    1922            0 :          }
    1923              : 
    1924              :          // find key or subdirectory to save
    1925            0 :          status = db_find_key(c->hDB, 0, odbpath, &hKey);
    1926            0 :          if (status != DB_SUCCESS) {
    1927            0 :             seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
    1928            0 :             return;
    1929              :          } else {
    1930            0 :             if (strstr(value.c_str(), ".json") || strstr(value.c_str(), ".JSON") || strstr(value.c_str(), ".js") || strstr(value.c_str(), ".JS"))
    1931            0 :                status = db_save_json(c->hDB, hKey, value.c_str(), JSFLAG_RECURSE | JSFLAG_OMIT_LAST_WRITTEN | JSFLAG_FOLLOW_LINKS);
    1932            0 :             else if  (strstr(value.c_str(), ".xml") || strstr(value.c_str(), ".XML"))
    1933            0 :                status = db_save_xml(c->hDB, hKey, value.c_str());
    1934              :             else
    1935            0 :                status = db_save(c->hDB, hKey, value.c_str(), FALSE);
    1936            0 :             if (status != DB_SUCCESS) {
    1937            0 :                seq_error(seq, c, msprintf("Cannot save file \"%s\", error %d", value.c_str(), status).c_str());
    1938            0 :                return;
    1939              :             }
    1940              :          }
    1941              :       } else {
    1942            0 :          seq_error(seq, c, "No ODB path specified in ODBSAVE command");
    1943            0 :          return;
    1944              :       }
    1945              : 
    1946            0 :       if (status == DB_SUCCESS) {
    1947            0 :          size = sizeof(seq);
    1948            0 :          db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
    1949            0 :          seq.current_line_number++;
    1950            0 :       } else if (status == DB_FILE_ERROR) {
    1951            0 :          seq_error(seq, c, msprintf("Error reading file \"%s\"", value.c_str()).c_str());
    1952              :       } else {
    1953              :          //something went really wrong
    1954            0 :          seq_error(seq, c, "Internal error loading ODB file!");
    1955            0 :          return;
    1956              :       }
    1957              :    }
    1958              : 
    1959              :    /*---- ODBGet ----*/
    1960            0 :    else if (equal_ustring(mxml_get_name(pn), "ODBGet")) {
    1961            0 :       if (!mxml_get_attribute(pn, "path")) {
    1962            0 :          seq_error(seq, c, "Missing attribute \"path\"");
    1963              :       } else {
    1964            0 :          mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
    1965            0 :          if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
    1966            0 :             mstrlcat(odbpath, "/", sizeof(odbpath));
    1967            0 :          mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
    1968              : 
    1969            0 :          if (strchr(odbpath, '$')) {
    1970            0 :             if (strchr(odbpath, '[')) {
    1971              :                // keep $ in index for later evaluation
    1972            0 :                std::string s(odbpath);
    1973            0 :                std::string s1 = s.substr(0, s.find('['));
    1974            0 :                std::string s2 = s.substr(s.find('['));
    1975            0 :                s1 = eval_var(seq, c, s1);
    1976            0 :                s1 += s2;
    1977            0 :                mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
    1978            0 :             } else {
    1979              :                // evaluate variable in path
    1980            0 :                std::string s(odbpath);
    1981            0 :                s = eval_var(seq, c, s);
    1982            0 :                mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
    1983            0 :             }
    1984              :          }
    1985              : 
    1986              :          /* check if index is supplied */
    1987            0 :          index1 = index2 = 0;
    1988            0 :          seq_array_index(seq, c, odbpath, &index1, &index2);
    1989              : 
    1990            0 :          mstrlcpy(name, mxml_get_value(pn), sizeof(name));
    1991            0 :          status = db_find_key(c->hDB, 0, odbpath, &hKey);
    1992            0 :          if (status != DB_SUCCESS) {
    1993            0 :             seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
    1994            0 :             return;
    1995              :          } else {
    1996            0 :             db_get_key(c->hDB, hKey, &key);
    1997            0 :             size = sizeof(data);
    1998              : 
    1999            0 :             status = db_get_data_index(c->hDB, hKey, data, &size, index1, key.type);
    2000            0 :             if (key.type == TID_BOOL)
    2001            0 :                value = *((int *) data) > 0 ? "1" : "0";
    2002              :             else
    2003            0 :                value = db_sprintf(data, size, 0, key.type);
    2004              : 
    2005            0 :             sprintf(str, "Variables/%s", name); // FIXME: unsafe
    2006            0 :             size = value.length() + 1;
    2007            0 :             if (size < 32)
    2008            0 :                size = 32;
    2009            0 :             db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
    2010              : 
    2011            0 :             size = sizeof(seq);
    2012            0 :             db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
    2013            0 :             seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2014              :          }
    2015              :       }
    2016              :    }
    2017              : 
    2018              :    /*---- ODBLookup ----*/
    2019            0 :    else if (equal_ustring(mxml_get_name(pn), "ODBLookup")) {
    2020            0 :       if (!mxml_get_attribute(pn, "path")) {
    2021            0 :          seq_error(seq, c, "Missing attribute \"path\"");
    2022            0 :       } else if (!mxml_get_attribute(pn, "string")) {
    2023            0 :          seq_error(seq, c, "Missing attribute \"string\"");
    2024              :       } else {
    2025            0 :          mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
    2026            0 :          if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
    2027            0 :             mstrlcat(odbpath, "/", sizeof(odbpath));
    2028            0 :          mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
    2029              : 
    2030            0 :          if (strchr(odbpath, '$')) {
    2031            0 :             if (strchr(odbpath, '[')) {
    2032              :                // keep $ in index for later evaluation
    2033            0 :                std::string s(odbpath);
    2034            0 :                std::string s1 = s.substr(0, s.find('['));
    2035            0 :                std::string s2 = s.substr(s.find('['));
    2036            0 :                s1 = eval_var(seq, c, s1);
    2037            0 :                s1 += s2;
    2038            0 :                mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
    2039            0 :             } else {
    2040              :                // evaluate variable in path
    2041            0 :                std::string s(odbpath);
    2042            0 :                s = eval_var(seq, c, s);
    2043            0 :                mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
    2044            0 :             }
    2045              :          }
    2046              :          
    2047            0 :          mstrlcpy(name, mxml_get_value(pn), sizeof(name));
    2048              : 
    2049            0 :          auto s = eval_var(seq, c, mxml_get_attribute(pn, "string"));
    2050            0 :          mstrlcpy(str, s.c_str(), sizeof(str));
    2051              : 
    2052            0 :          status = db_find_key(c->hDB, 0, odbpath, &hKey);
    2053            0 :          if (status != DB_SUCCESS) {
    2054            0 :             seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
    2055            0 :             return;
    2056              :          } else {
    2057            0 :             db_get_key(c->hDB, hKey, &key);
    2058              : 
    2059              :             // search for "str"
    2060            0 :             for (i = 0 ; i<key.num_values ; i++) {
    2061            0 :                size = sizeof(data);
    2062            0 :                status = db_get_data_index(c->hDB, hKey, data, &size, i, key.type);
    2063            0 :                if (strcmp((const char *)data, str) == 0)
    2064            0 :                   break;
    2065              :             }
    2066              : 
    2067            0 :             size = sizeof(i);
    2068            0 :             if (i < key.num_values)
    2069            0 :                value = db_sprintf(&i, size, 0, TID_INT);
    2070              :             else {
    2071            0 :                snprintf(str, sizeof(str), "\"ODBLOOKUP %s\" did not find string \"%s\"", odbpath, s.c_str());
    2072            0 :                seq_error(seq, c, str);
    2073            0 :                return;
    2074              :             }
    2075              : 
    2076            0 :             snprintf(str, sizeof(str), "Variables/%s", name);
    2077            0 :             size = value.length() + 1;
    2078            0 :             if (size < 32)
    2079            0 :                size = 32;
    2080            0 :             db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
    2081              : 
    2082            0 :             size = sizeof(seq);
    2083            0 :             db_get_record1(c->hDB, hKeySeq, &seq, &size, 0, strcomb1(sequencer_str).c_str());// could have changed seq tree
    2084            0 :             seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2085              :          }
    2086            0 :       }
    2087              :    }
    2088              : 
    2089              :    /*---- ODBInc ----*/
    2090            0 :    else if (equal_ustring(mxml_get_name(pn), "ODBInc")) {
    2091            0 :       if (!mxml_get_attribute(pn, "path")) {
    2092            0 :          seq_error(seq, c, "Missing attribute \"path\"");
    2093              :       } else {
    2094            0 :          mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
    2095            0 :          if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
    2096            0 :             mstrlcat(odbpath, "/", sizeof(odbpath));
    2097            0 :          mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
    2098              : 
    2099            0 :          if (strchr(odbpath, '$')) {
    2100            0 :             if (strchr(odbpath, '[')) {
    2101              :                // keep $ in index for later evaluation
    2102            0 :                std::string s(odbpath);
    2103            0 :                std::string s1 = s.substr(0, s.find('['));
    2104            0 :                std::string s2 = s.substr(s.find('['));
    2105            0 :                s1 = eval_var(seq, c, s1);
    2106            0 :                s1 += s2;
    2107            0 :                mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
    2108            0 :             } else {
    2109              :                // evaluate variable in path
    2110            0 :                std::string s(odbpath);
    2111            0 :                s = eval_var(seq, c, s);
    2112            0 :                mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
    2113            0 :             }
    2114              :          }
    2115              : 
    2116            0 :          index1 = index2 = 0;
    2117            0 :          seq_array_index(seq, c, odbpath, &index1, &index2);
    2118              : 
    2119            0 :          value = eval_var(seq, c, mxml_get_value(pn));
    2120              : 
    2121            0 :          status = db_find_key(c->hDB, 0, odbpath, &hKey);
    2122            0 :          if (status != DB_SUCCESS) {
    2123            0 :             seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
    2124              :          } else {
    2125            0 :             db_get_key(c->hDB, hKey, &key);
    2126            0 :             size = sizeof(data);
    2127            0 :             db_get_data_index(c->hDB, hKey, data, &size, index1, key.type);
    2128            0 :             std::string s = db_sprintf(data, size, 0, key.type);
    2129            0 :             d = std::stof(s);
    2130            0 :             d += std::stof(value);
    2131            0 :             sprintf(str, "%lg", d);
    2132            0 :             size = sizeof(data);
    2133            0 :             db_sscanf(str, data, &size, 0, key.type);
    2134              : 
    2135            0 :             int notify = TRUE;
    2136            0 :             if (seq.subdir_not_notify)
    2137            0 :                notify = FALSE;
    2138            0 :             if (mxml_get_attribute(pn, "notify"))
    2139            0 :                notify = atoi(mxml_get_attribute(pn, "notify"));
    2140              : 
    2141            0 :             db_set_data_index1(c->hDB, hKey, data, key.item_size, index1, key.type, notify);
    2142            0 :             seq.current_line_number++;
    2143            0 :          }
    2144              :       }
    2145              :    }
    2146              : 
    2147              :    /*---- ODBDelete ----*/
    2148            0 :    else if (equal_ustring(mxml_get_name(pn), "ODBDelete")) {
    2149            0 :       mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
    2150            0 :       if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
    2151            0 :          mstrlcat(odbpath, "/", sizeof(odbpath));
    2152            0 :       mstrlcat(odbpath, mxml_get_value(pn), sizeof(odbpath));
    2153              : 
    2154            0 :       if (strchr(odbpath, '$')) {
    2155            0 :          std::string s(odbpath);
    2156            0 :          s = eval_var(seq, c, s);
    2157            0 :          mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
    2158            0 :       }
    2159              : 
    2160            0 :       status = db_find_key(c->hDB, 0, odbpath, &hKey);
    2161            0 :       if (status != DB_SUCCESS) {
    2162            0 :          seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
    2163              :       } else {
    2164            0 :          status = db_delete_key(c->hDB, hKey, FALSE);
    2165            0 :          if (status != DB_SUCCESS) {
    2166            0 :             seq_error(seq, c, msprintf("Cannot delete ODB key \"%s\"", odbpath).c_str());
    2167              :          } else
    2168            0 :             seq.current_line_number++;
    2169              :       }
    2170              :    }
    2171              : 
    2172              :    /*---- ODBCreate ----*/
    2173            0 :    else if (equal_ustring(mxml_get_name(pn), "ODBCreate")) {
    2174            0 :       if (!mxml_get_attribute(pn, "path")) {
    2175            0 :          seq_error(seq, c, "Missing attribute \"path\"");
    2176            0 :       } else if (!mxml_get_attribute(pn, "type")) {
    2177            0 :          seq_error(seq, c, "Missing attribute \"type\"");
    2178              :       } else {
    2179            0 :          mstrlcpy(odbpath, seq.subdir, sizeof(odbpath));
    2180            0 :          if (strlen(odbpath) > 0 && odbpath[strlen(odbpath) - 1] != '/')
    2181            0 :             mstrlcat(odbpath, "/", sizeof(odbpath));
    2182            0 :          mstrlcat(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
    2183              : 
    2184            0 :          if (strchr(odbpath, '$')) {
    2185            0 :             std::string s(odbpath);
    2186            0 :             s = eval_var(seq, c, s);
    2187            0 :             mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
    2188            0 :          }
    2189              : 
    2190              :          /* get TID */
    2191              :          unsigned int tid;
    2192            0 :          for (tid = 0; tid < TID_LAST; tid++) {
    2193            0 :             if (equal_ustring(rpc_tid_name(tid), mxml_get_attribute(pn, "type")))
    2194            0 :                break;
    2195              :          }
    2196              : 
    2197            0 :          if (tid == TID_LAST)
    2198            0 :             seq_error(seq, c, "Type must be one of UINT8,INT8,UINT16,INT16,UINT32,INT32,BOOL,FLOAT,DOUBLE,STRING");
    2199              :          else {
    2200              : 
    2201            0 :             status = db_find_key(c->hDB, 0, odbpath, &hKey);
    2202            0 :             if (status == DB_SUCCESS) {
    2203            0 :                db_get_key(c->hDB, hKey, &key);
    2204            0 :                if (key.type != tid) {
    2205            0 :                   db_delete_key(c->hDB, hKey, FALSE);
    2206            0 :                   status = db_create_key(c->hDB, 0, odbpath, tid);
    2207              :                }
    2208              :             } else {
    2209            0 :                status = db_create_key(c->hDB, 0, odbpath, tid);
    2210              :                char dummy[32];
    2211            0 :                memset(dummy, 0, sizeof(dummy));
    2212            0 :                if (tid == TID_STRING || tid == TID_LINK)
    2213            0 :                   db_set_value(c->hDB, 0, odbpath, dummy, 32, 1, tid);
    2214              :             }
    2215              : 
    2216            0 :             if (status != DB_SUCCESS && status != DB_CREATED) {
    2217            0 :                seq_error(seq, c, msprintf("Cannot create ODB key \"%s\", error code %d", odbpath, status).c_str());
    2218              :             } else {
    2219            0 :                status = db_find_key(c->hDB, 0, odbpath, &hKey);
    2220            0 :                if (mxml_get_attribute(pn, "size")) {
    2221            0 :                   i = atoi(eval_var(seq, c, mxml_get_attribute(pn, "size")).c_str());
    2222            0 :                   if (i > 1)
    2223            0 :                      db_set_num_values(c->hDB, hKey, i);
    2224              :                }
    2225            0 :                seq.current_line_number++;
    2226              :             }
    2227              :          }
    2228              :       }
    2229              :    }
    2230              : 
    2231              :    /*---- RunDescription ----*/
    2232            0 :    else if (equal_ustring(mxml_get_name(pn), "RunDescription")) {
    2233            0 :       db_set_value(c->hDB, 0, "/Experiment/Run Parameters/Run Description", mxml_get_value(pn), 256, 1, TID_STRING);
    2234            0 :       seq.current_line_number++;
    2235              :    }
    2236              : 
    2237              :    /*---- Script ----*/
    2238            0 :    else if (equal_ustring(mxml_get_name(pn), "Script")) {
    2239            0 :       sprintf(str, "%s", mxml_get_value(pn)); // FIXME: unsafe
    2240              : 
    2241            0 :       if (mxml_get_attribute(pn, "params")) {
    2242            0 :          mstrlcpy(data, mxml_get_attribute(pn, "params"), sizeof(data));
    2243            0 :          n = strbreak(data, list, 100, ",", FALSE);
    2244            0 :          for (i = 0; i < n; i++) {
    2245              : 
    2246            0 :             value = eval_var(seq, c, list[i]);
    2247              : 
    2248            0 :             mstrlcat(str, " ", sizeof(str));
    2249            0 :             mstrlcat(str, value.c_str(), sizeof(str));
    2250              :          }
    2251              :       }
    2252              : 
    2253            0 :       std::string s(str);
    2254            0 :       s = ss_replace_env_variables(s);
    2255            0 :       std::string r = ss_execs(s.c_str());
    2256              : 
    2257              :       // put result into SCRIPT_RESULT variable
    2258            0 :       db_set_value(c->hDB, c->hSeq, "Variables/SCRIPT_RESULT", r.c_str(), r.length(), 1, TID_STRING);
    2259              : 
    2260            0 :       seq.current_line_number++;
    2261            0 :    }
    2262              : 
    2263              :    /*---- Transition ----*/
    2264            0 :    else if (equal_ustring(mxml_get_name(pn), "Transition")) {
    2265            0 :       if (equal_ustring(mxml_get_value(pn), "Start")) {
    2266            0 :          if (!seq.transition_request) {
    2267            0 :             seq.transition_request = TRUE;
    2268            0 :             size = sizeof(state);
    2269            0 :             db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
    2270            0 :             if (state != STATE_RUNNING) {
    2271            0 :                size = sizeof(run_number);
    2272            0 :                db_get_value(c->hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT32, FALSE);
    2273            0 :                status = cm_transition(TR_START, run_number + 1, str, sizeof(str), TR_MTHREAD | TR_SYNC, FALSE);
    2274            0 :                if (status != CM_SUCCESS) {
    2275            0 :                   seq_error(seq, c, msprintf("Cannot start run: %s", str).c_str());
    2276              :                }
    2277              :             }
    2278              :          } else {
    2279              :             // Wait until transition has finished
    2280            0 :             size = sizeof(state);
    2281            0 :             db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
    2282            0 :             if (state == STATE_RUNNING) {
    2283            0 :                seq.transition_request = FALSE;
    2284            0 :                seq.current_line_number++;
    2285              :             }
    2286              :          }
    2287            0 :       } else if (equal_ustring(mxml_get_value(pn), "Stop")) {
    2288            0 :          if (!seq.transition_request) {
    2289            0 :             seq.transition_request = TRUE;
    2290            0 :             size = sizeof(state);
    2291            0 :             db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
    2292            0 :             if (state != STATE_STOPPED) {
    2293            0 :                status = cm_transition(TR_STOP, 0, str, sizeof(str), TR_MTHREAD | TR_SYNC, FALSE);
    2294            0 :                if (status == CM_DEFERRED_TRANSITION) {
    2295              :                   // do nothing
    2296            0 :                } else if (status != CM_SUCCESS) {
    2297            0 :                   seq_error(seq, c, msprintf("Cannot stop run: %s", str).c_str());
    2298              :                }
    2299              :             }
    2300              :          } else {
    2301              :             // Wait until transition has finished
    2302            0 :             size = sizeof(state);
    2303            0 :             db_get_value(c->hDB, 0, "/Runinfo/State", &state, &size, TID_INT32, FALSE);
    2304            0 :             if (state == STATE_STOPPED) {
    2305            0 :                size = sizeof(seq);
    2306            0 :                db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
    2307              : 
    2308            0 :                seq.transition_request = FALSE;
    2309              : 
    2310            0 :                if (seq.stop_after_run) {
    2311            0 :                   seq.stop_after_run = FALSE;
    2312              : 
    2313            0 :                   if (!goto_at_exit(seq, c)) {
    2314            0 :                      seq.running = FALSE;
    2315            0 :                      seq.finished = TRUE;
    2316            0 :                      seq_stop(seq, c);
    2317            0 :                      cm_msg(MTALK, "sequencer", "Sequencer is finished by \"stop after current run\".");
    2318              :                   } else {
    2319            0 :                      cm_msg(MTALK, "sequencer", "Sequencer is going to finish by \"stop after current run\". Executing ATEXIT.");
    2320              :                   }
    2321              :                } else {
    2322            0 :                   seq.current_line_number++;
    2323              :                }
    2324              : 
    2325            0 :                db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
    2326              :             }
    2327              :          }
    2328              :       } else {
    2329            0 :          seq_error(seq, c, msprintf("Invalid transition \"%s\"", mxml_get_value(pn)).c_str());
    2330            0 :          return;
    2331              :       }
    2332              :    }
    2333              : 
    2334              :    /*---- Wait ----*/
    2335            0 :    else if (equal_ustring(mxml_get_name(pn), "Wait")) {
    2336            0 :       if (equal_ustring(mxml_get_attribute(pn, "for"), "Events")) {
    2337            0 :          n = atoi(eval_var(seq, c, mxml_get_value(pn)).c_str());
    2338            0 :          seq.wait_limit = (float) n;
    2339            0 :          strcpy(seq.wait_type, "Events");
    2340            0 :          size = sizeof(d);
    2341            0 :          db_get_value(c->hDB, 0, "/Equipment/Trigger/Statistics/Events sent", &d, &size, TID_DOUBLE, FALSE);
    2342            0 :          seq.wait_value = (float) d;
    2343            0 :          if (d >= n) {
    2344            0 :             seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2345            0 :             seq.wait_limit = 0;
    2346            0 :             seq.wait_value = 0;
    2347            0 :             seq.wait_type[0] = 0;
    2348            0 :             seq.wait_odb[0] = 0;
    2349              :          }
    2350            0 :          seq.wait_value = (float) d;
    2351            0 :       } else if (equal_ustring(mxml_get_attribute(pn, "for"), "ODBValue")) {
    2352            0 :          seq.wait_limit = (float) atof(eval_var(seq, c, mxml_get_value(pn)).c_str());
    2353            0 :          mstrlcpy(seq.wait_type, "ODB", sizeof(seq.wait_type));
    2354            0 :          if (!mxml_get_attribute(pn, "path")) {
    2355            0 :             seq_error(seq, c, "\"path\" must be given for ODB values");
    2356            0 :             return;
    2357              :          } else {
    2358            0 :             mstrlcpy(odbpath, mxml_get_attribute(pn, "path"), sizeof(odbpath));
    2359            0 :             mstrlcpy(seq.wait_odb, odbpath, sizeof(seq.wait_odb));
    2360              : 
    2361            0 :             if (strchr(odbpath, '$')) {
    2362            0 :                if (strchr(odbpath, '[')) {
    2363              :                   // keep $ in index for later evaluation
    2364            0 :                   std::string s(odbpath);
    2365            0 :                   std::string s1 = s.substr(0, s.find('['));
    2366            0 :                   std::string s2 = s.substr(s.find('['));
    2367            0 :                   s1 = eval_var(seq, c, s1);
    2368            0 :                   s1 += s2;
    2369            0 :                   mstrlcpy(odbpath, s1.c_str(), sizeof(odbpath));
    2370            0 :                } else {
    2371              :                   // evaluate variable in path
    2372            0 :                   std::string s(odbpath);
    2373            0 :                   s = eval_var(seq, c, s);
    2374            0 :                   mstrlcpy(odbpath, s.c_str(), sizeof(odbpath));
    2375            0 :                }
    2376              :             }
    2377              : 
    2378            0 :             index1 = index2 = 0;
    2379            0 :             seq_array_index(seq, c, odbpath, &index1, &index2);
    2380            0 :             status = db_find_key(c->hDB, 0, odbpath, &hKey);
    2381            0 :             if (status != DB_SUCCESS) {
    2382            0 :                seq_error(seq, c, msprintf("Cannot find ODB key \"%s\"", odbpath).c_str());
    2383            0 :                return;
    2384              :             } else {
    2385            0 :                if (mxml_get_attribute(pn, "op"))
    2386            0 :                   mstrlcpy(op, mxml_get_attribute(pn, "op"), sizeof(op));
    2387              :                else
    2388            0 :                   strcpy(op, "!=");
    2389            0 :                mstrlcat(seq.wait_type, op, sizeof(seq.wait_type));
    2390              : 
    2391            0 :                db_get_key(c->hDB, hKey, &key);
    2392            0 :                size = sizeof(data);
    2393            0 :                db_get_data_index(c->hDB, hKey, data, &size, index1, key.type);
    2394            0 :                if (key.type == TID_BOOL)
    2395            0 :                   value = *((int *) data) > 0 ? "1" : "0";
    2396              :                else
    2397            0 :                   value = db_sprintf(data, size, 0, key.type);
    2398            0 :                cont = FALSE;
    2399            0 :                seq.wait_value = std::stof(value);
    2400            0 :                if (equal_ustring(op, ">=")) {
    2401            0 :                   cont = (seq.wait_value >= seq.wait_limit);
    2402            0 :                } else if (equal_ustring(op, ">")) {
    2403            0 :                   cont = (seq.wait_value > seq.wait_limit);
    2404            0 :                } else if (equal_ustring(op, "<=")) {
    2405            0 :                   cont = (seq.wait_value <= seq.wait_limit);
    2406            0 :                } else if (equal_ustring(op, "<")) {
    2407            0 :                   cont = (seq.wait_value < seq.wait_limit);
    2408            0 :                } else if (equal_ustring(op, "==")) {
    2409            0 :                   cont = (seq.wait_value == seq.wait_limit);
    2410            0 :                } else if (equal_ustring(op, "!=")) {
    2411            0 :                   cont = (seq.wait_value != seq.wait_limit);
    2412              :                } else {
    2413            0 :                   seq_error(seq, c, msprintf("Invalid comaprison \"%s\"", op).c_str());
    2414            0 :                   return;
    2415              :                }
    2416              : 
    2417            0 :                if (cont) {
    2418            0 :                   seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2419            0 :                   seq.wait_limit = 0;
    2420            0 :                   seq.wait_value = 0;
    2421            0 :                   seq.wait_type[0] = 0;
    2422            0 :                   seq.wait_odb[0] = 0;
    2423              :                }
    2424              :             }
    2425              :          }
    2426            0 :       } else if (equal_ustring(mxml_get_attribute(pn, "for"), "Seconds")) {
    2427            0 :          seq.wait_limit = 1000.0f * (float) atof(eval_var(seq, c, mxml_get_value(pn)).c_str());
    2428            0 :          strcpy(seq.wait_type, "Seconds");
    2429            0 :          if (seq.start_time == 0) {
    2430            0 :             seq.start_time = ss_millitime();
    2431            0 :             seq.wait_value = 0;
    2432              :          } else {
    2433            0 :             seq.wait_value = (float) (ss_millitime() - seq.start_time);
    2434            0 :             if (seq.wait_value > seq.wait_limit)
    2435            0 :                seq.wait_value = seq.wait_limit;
    2436              :          }
    2437            0 :          if (ss_millitime() - seq.start_time > (DWORD) seq.wait_limit) {
    2438            0 :             seq.current_line_number++;
    2439            0 :             seq.start_time = 0;
    2440            0 :             seq.wait_limit = 0;
    2441            0 :             seq.wait_value = 0;
    2442            0 :             seq.wait_type[0] = 0;
    2443            0 :             seq.wait_odb[0] = 0;
    2444              :          }
    2445              :       } else {
    2446            0 :          seq_error(seq, c, msprintf("Invalid wait attribute \"%s\"", mxml_get_attribute(pn, "for")).c_str());
    2447              :       }
    2448              : 
    2449              :       // sleep to keep the CPU from consuming 100%
    2450            0 :       ss_sleep(1);
    2451              :    }
    2452              : 
    2453              :    /*---- Loop start ----*/
    2454            0 :    else if (equal_ustring(mxml_get_name(pn), "Loop")) {
    2455            0 :       for (i = 0; i < SEQ_NEST_LEVEL_LOOP; i++)
    2456            0 :          if (seq.loop_start_line[i] == 0)
    2457            0 :             break;
    2458            0 :       if (i == SEQ_NEST_LEVEL_LOOP) {
    2459            0 :          seq_error(seq, c, "Maximum loop nesting exceeded");
    2460            0 :          return;
    2461              :       }
    2462            0 :       seq.loop_start_line[i] = seq.current_line_number;
    2463            0 :       seq.loop_end_line[i] = mxml_get_line_number_end(pn);
    2464            0 :       if (mxml_get_attribute(pn, "l"))
    2465            0 :          seq.sloop_start_line[i] = atoi(mxml_get_attribute(pn, "l"));
    2466            0 :       if (mxml_get_attribute(pn, "le"))
    2467            0 :          seq.sloop_end_line[i] = atoi(mxml_get_attribute(pn, "le"));
    2468            0 :       seq.loop_counter[i] = 1;
    2469              : 
    2470            0 :       if (mxml_get_attribute(pn, "n")) {
    2471            0 :          if (equal_ustring(mxml_get_attribute(pn, "n"), "infinite"))
    2472            0 :             seq.loop_n[i] = -1;
    2473              :          else {
    2474            0 :             seq.loop_n[i] = atoi(eval_var(seq, c, mxml_get_attribute(pn, "n")).c_str());
    2475              :          }
    2476            0 :          value = "1";
    2477            0 :       } else if (mxml_get_attribute(pn, "values")) {
    2478            0 :          mstrlcpy(data, mxml_get_attribute(pn, "values"), sizeof(data));
    2479            0 :          seq.loop_n[i] = strbreak(data, list, 100, ",", FALSE);
    2480            0 :          value = eval_var(seq, c, list[0]);
    2481              :       } else {
    2482            0 :          seq_error(seq, c, "Missing \"var\" or \"n\" attribute");
    2483            0 :          return;
    2484              :       }
    2485              : 
    2486            0 :       if (mxml_get_attribute(pn, "var")) {
    2487            0 :          mstrlcpy(name, mxml_get_attribute(pn, "var"), sizeof(name));
    2488            0 :          sprintf(str, "Variables/%s", name); // FIXME: unsafe
    2489            0 :          size = value.length() + 1;
    2490            0 :          if (size < 32)
    2491            0 :             size = 32;
    2492            0 :          db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
    2493              :       }
    2494              : 
    2495            0 :       seq.current_line_number++;
    2496              :    }
    2497              : 
    2498              :    /*---- Break ----*/
    2499            0 :    else if (equal_ustring(mxml_get_name(pn), "Break")) {
    2500              : 
    2501              :       // finish current if statement
    2502            0 :       while (seq.if_index > 0 &&
    2503            0 :              seq.current_line_number > seq.if_line[seq.if_index - 1] &&
    2504            0 :              seq.current_line_number < seq.if_endif_line[seq.if_index-1]) {
    2505            0 :          size = sizeof(seq);
    2506            0 :          db_get_record(c->hDB, hKeySeq, &seq, &size, 0);
    2507            0 :          seq.if_index--;
    2508            0 :          seq.if_line[seq.if_index] = 0;
    2509            0 :          seq.if_else_line[seq.if_index] = 0;
    2510            0 :          seq.if_endif_line[seq.if_index] = 0;
    2511            0 :          seq.current_line_number++;
    2512            0 :          db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
    2513              :       }
    2514              : 
    2515              :       // goto next loop end
    2516            0 :       for (i = 0; i < SEQ_NEST_LEVEL_LOOP; i++)
    2517            0 :          if (seq.loop_start_line[i] == 0)
    2518            0 :             break;
    2519            0 :       if (i == 0) {
    2520            0 :          seq_error(seq, c, "\"Break\" outside any loop");
    2521            0 :          return;
    2522              :       }
    2523              : 
    2524              :       // force end of loop in next check
    2525            0 :       seq.current_line_number = seq.loop_end_line[i-1];
    2526            0 :       seq.loop_counter[i-1] = seq.loop_n[i-1];
    2527              :    }
    2528              : 
    2529              :    /*---- If ----*/
    2530            0 :    else if (equal_ustring(mxml_get_name(pn), "If")) {
    2531              : 
    2532            0 :       if (seq.if_index == SEQ_NEST_LEVEL_IF) {
    2533            0 :          seq_error(seq, c, "Maximum number of nested if..endif exceeded");
    2534            0 :          return;
    2535              :       }
    2536              : 
    2537              :       // store "if" and "endif" lines
    2538            0 :       seq.if_line[seq.if_index] = seq.current_line_number;
    2539            0 :       seq.if_endif_line[seq.if_index] = mxml_get_line_number_end(pn);
    2540              : 
    2541              :       // search for "else" line
    2542            0 :       seq.if_else_line[seq.if_index] = 0;
    2543            0 :       for (j = seq.current_line_number + 1; j < mxml_get_line_number_end(pn) + 1; j++) {
    2544            0 :          pe = mxml_get_node_at_line(c->pnseq, j);
    2545              : 
    2546              :          // skip nested if..endif
    2547            0 :          if (pe && equal_ustring(mxml_get_name(pe), "If")) {
    2548            0 :             j = mxml_get_line_number_end(pe);
    2549            0 :             continue;
    2550              :          }
    2551              : 
    2552              :          // store "else" if found
    2553            0 :          if (pe && equal_ustring(mxml_get_name(pe), "Else")) {
    2554            0 :             seq.if_else_line[seq.if_index] = j;
    2555            0 :             break;
    2556              :          }
    2557              :       }
    2558              : 
    2559            0 :       mstrlcpy(str, mxml_get_attribute(pn, "condition"), sizeof(str));
    2560            0 :       i = eval_condition(seq, c, str);
    2561            0 :       if (i < 0) {
    2562            0 :          seq_error(seq, c, "Invalid number in comparison");
    2563            0 :          return;
    2564              :       }
    2565              : 
    2566            0 :       if (i == 1)
    2567            0 :          seq.current_line_number++;
    2568            0 :       else if (seq.if_else_line[seq.if_index])
    2569            0 :          seq.current_line_number = seq.if_else_line[seq.if_index] + 1;
    2570              :       else
    2571            0 :          seq.current_line_number = seq.if_endif_line[seq.if_index];
    2572              : 
    2573            0 :       seq.if_index++;
    2574              :    }
    2575              : 
    2576              :    /*---- Else ----*/
    2577            0 :    else if (equal_ustring(mxml_get_name(pn), "Else")) {
    2578              :       // goto next "Endif"
    2579            0 :       if (seq.if_index == 0) {
    2580            0 :          seq_error(seq, c, "Unexpected Else");
    2581            0 :          return;
    2582              :       }
    2583            0 :       seq.current_line_number = seq.if_endif_line[seq.if_index - 1];
    2584              :    }
    2585              : 
    2586              :    /*---- Exit ----*/
    2587            0 :    else if (equal_ustring(mxml_get_name(pn), "Exit")) {
    2588            0 :       seq_stop(seq, c);
    2589            0 :       cm_msg(MTALK, "sequencer", "Sequencer is finished.");
    2590            0 :       return;
    2591              :    }
    2592              : 
    2593              :    /*---- Goto ----*/
    2594            0 :    else if (equal_ustring(mxml_get_name(pn), "Goto")) {
    2595            0 :       if (!mxml_get_attribute(pn, "line") && !mxml_get_attribute(pn, "sline")) {
    2596            0 :          seq_error(seq, c, "Missing line number");
    2597            0 :          return;
    2598              :       }
    2599            0 :       if (mxml_get_attribute(pn, "line")) {
    2600            0 :          seq.current_line_number = atoi(eval_var(seq, c, mxml_get_attribute(pn, "line")).c_str());
    2601              :       }
    2602            0 :       if (mxml_get_attribute(pn, "sline")) {
    2603            0 :          mstrlcpy(str, eval_var(seq, c, mxml_get_attribute(pn, "sline")).c_str(), sizeof(str));
    2604            0 :          for (i = 0; i < last_line; i++) {
    2605            0 :             pt = mxml_get_node_at_line(c->pnseq, i);
    2606            0 :             if (pt && mxml_get_attribute(pt, "l")) {
    2607            0 :                l = atoi(mxml_get_attribute(pt, "l"));
    2608            0 :                if (atoi(str) == l) {
    2609            0 :                   seq.current_line_number = i;
    2610            0 :                   break;
    2611              :                }
    2612              :             }
    2613              :          }
    2614              :       }
    2615              :    }
    2616              : 
    2617              :    /*---- Library ----*/
    2618            0 :    else if (equal_ustring(mxml_get_name(pn), "Library")) {
    2619              :       // simply skip libraries
    2620            0 :       seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2621              :    }
    2622              : 
    2623              :    /*---- Subroutine ----*/
    2624            0 :    else if (equal_ustring(mxml_get_name(pn), "Subroutine")) {
    2625              :       // simply skip subroutines
    2626            0 :       seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2627              :    }
    2628              : 
    2629              :    /*---- Param ----*/
    2630            0 :    else if (equal_ustring(mxml_get_name(pn), "Param")) {
    2631              :       // simply skip parameters
    2632            0 :       seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2633              :    }
    2634              : 
    2635              :    /*---- Set ----*/
    2636            0 :    else if (equal_ustring(mxml_get_name(pn), "Set")) {
    2637            0 :       if (!mxml_get_attribute(pn, "name")) {
    2638            0 :          seq_error(seq, c, "Missing variable name");
    2639            0 :          return;
    2640              :       }
    2641            0 :       mstrlcpy(name, mxml_get_attribute(pn, "name"), sizeof(name));
    2642            0 :       value = eval_var(seq, c, mxml_get_value(pn));
    2643              : 
    2644            0 :       if (strchr(name, '[')) {
    2645              :          // array
    2646            0 :          mstrlcpy(str, strchr(name, '[')+1, sizeof(str));
    2647            0 :          if (strchr(str, ']'))
    2648            0 :             *strchr(str, ']') = 0;
    2649            0 :          int index = atoi(eval_var(seq, c, str).c_str());
    2650            0 :          *strchr(name, '[') = 0;
    2651            0 :          sprintf(str, "Variables/%s", name); // FIXME: unsafe
    2652            0 :          status = db_find_key(c->hDB, c->hSeq, str, &hKey);
    2653            0 :          if (status != DB_SUCCESS) {
    2654            0 :             db_create_key(c->hDB, c->hSeq, str, TID_STRING);
    2655            0 :             status = db_find_key(c->hDB, c->hSeq, str, &hKey);
    2656              :          }
    2657            0 :          size = value.length() + 1;
    2658            0 :          if (size < 32)
    2659            0 :             size = 32;
    2660            0 :          status = db_set_data_index(c->hDB, hKey, value.c_str(), size, index, TID_STRING);
    2661              :       } else {
    2662            0 :          sprintf(str, "Variables/%s", name); // FIXME: unsafe
    2663            0 :          size = value.length() + 1;
    2664            0 :          if (size < 32)
    2665            0 :             size = 32;
    2666            0 :          db_set_value(c->hDB, c->hSeq, str, value.c_str(), size, 1, TID_STRING);
    2667              :       }
    2668              : 
    2669              :       // check if variable is used in loop
    2670            0 :       for (i = SEQ_NEST_LEVEL_LOOP-1; i >= 0; i--)
    2671            0 :          if (seq.loop_start_line[i] > 0) {
    2672            0 :             pr = mxml_get_node_at_line(c->pnseq, seq.loop_start_line[i]);
    2673            0 :             if (mxml_get_attribute(pr, "var")) {
    2674            0 :                if (equal_ustring(mxml_get_attribute(pr, "var"), name))
    2675            0 :                   seq.loop_counter[i] = atoi(value.c_str());
    2676              :             }
    2677              :          }
    2678              : 
    2679            0 :       seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2680              :    }
    2681              : 
    2682              :    /*---- Message ----*/
    2683            0 :    else if (equal_ustring(mxml_get_name(pn), "Message")) {
    2684            0 :       if (strchr(mxml_get_value(pn), '$')) // evaluate message string if $ present
    2685            0 :          value = eval_var(seq, c, mxml_get_value(pn));
    2686              :       else
    2687            0 :          value = mxml_get_value(pn); // treat message as string
    2688            0 :       const char *wait_attr = mxml_get_attribute(pn, "wait");
    2689            0 :       bool wait = false;
    2690            0 :       if (wait_attr)
    2691            0 :          wait = (atoi(wait_attr) == 1);
    2692              : 
    2693            0 :       if (!wait) {
    2694              :          // message with no wait: set seq.message and move on. we do not care if web page clears it
    2695            0 :          mstrlcpy(seq.message, value.c_str(), sizeof(seq.message));
    2696            0 :          seq.message_wait = FALSE;
    2697            0 :          db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
    2698              :       } else {
    2699              :          // message with wait
    2700              : 
    2701              :          // if message_wait not set, we are here for the first time
    2702            0 :          if (!seq.message_wait) {
    2703            0 :             mstrlcpy(seq.message, value.c_str(), sizeof(seq.message));
    2704            0 :             seq.message_wait = TRUE;
    2705            0 :             db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
    2706              :             // wait
    2707            0 :             return;
    2708              :          } else {
    2709              :             // message_wait is set, we have been here before
    2710              : 
    2711              :             // if web page did not clear the message, keep waiting
    2712            0 :             if (seq.message[0] != 0) {
    2713              :                // wait
    2714            0 :                return;
    2715              :             }
    2716              : 
    2717              :             // web page cleared the message, we are done with waiting
    2718            0 :             seq.message_wait = false;
    2719              :          }
    2720              :       }
    2721              : 
    2722            0 :       seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2723              :    }
    2724              : 
    2725              :    /*---- Msg ----*/
    2726            0 :    else if (equal_ustring(mxml_get_name(pn), "Msg")) {
    2727            0 :       if (strchr(mxml_get_value(pn), '$')) // evaluate message string if $ present
    2728            0 :          value = eval_var(seq, c, mxml_get_value(pn));
    2729              :       else
    2730            0 :          value = mxml_get_value(pn); // treat message as string
    2731            0 :       std::string type = "INFO";
    2732            0 :       if (mxml_get_attribute(pn, "type"))
    2733            0 :          type = std::string(mxml_get_attribute(pn, "type"));
    2734              : 
    2735            0 :       if (type == "ERROR")
    2736            0 :          cm_msg(MERROR, "sequencer", "%s", value.c_str());
    2737            0 :       else if (type == "DEBUG")
    2738            0 :          cm_msg(MDEBUG, "sequencer", "%s", value.c_str());
    2739            0 :       else if (type == "LOG")
    2740            0 :          cm_msg(MLOG, "sequencer", "%s", value.c_str());
    2741            0 :       else if (type == "TALK")
    2742            0 :          cm_msg(MTALK, "sequencer", "%s", value.c_str());
    2743              :       else
    2744            0 :          cm_msg(MINFO, "sequencer", "%s", value.c_str());
    2745              : 
    2746            0 :       seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2747            0 :    }
    2748              : 
    2749              :    /*---- Cat ----*/
    2750            0 :    else if (equal_ustring(mxml_get_name(pn), "Cat")) {
    2751            0 :       if (!mxml_get_attribute(pn, "name")) {
    2752            0 :          seq_error(seq, c, "Missing variable name");
    2753            0 :          return;
    2754              :       }
    2755            0 :       mstrlcpy(name, mxml_get_attribute(pn, "name"), sizeof(name));
    2756            0 :       if (!concatenate(seq, c, data, sizeof(data), mxml_get_value(pn)))
    2757            0 :          return;
    2758            0 :       sprintf(str, "Variables/%s", name); // FIXME: unsafe
    2759            0 :       size = strlen(data) + 1;
    2760            0 :       if (size < 32)
    2761            0 :          size = 32;
    2762            0 :       db_set_value(c->hDB, c->hSeq, str, data, size, 1, TID_STRING);
    2763              : 
    2764            0 :       seq.current_line_number = mxml_get_line_number_end(pn) + 1;
    2765              :    }
    2766              : 
    2767              :    /*---- Call ----*/
    2768            0 :    else if (equal_ustring(mxml_get_name(pn), "Call")) {
    2769            0 :       if (seq.stack_index == SEQ_NEST_LEVEL_SUB) {
    2770            0 :          seq_error(seq, c, "Maximum subroutine level exceeded");
    2771            0 :          return;
    2772              :       } else {
    2773              :          // put current line number on stack
    2774            0 :          seq.subroutine_call_line[seq.stack_index] = mxml_get_line_number_end(pn);
    2775            0 :          seq.ssubroutine_call_line[seq.stack_index] = atoi(mxml_get_attribute(pn, "l"));
    2776            0 :          seq.subroutine_return_line[seq.stack_index] = mxml_get_line_number_end(pn) + 1;
    2777              : 
    2778              :          // search subroutine
    2779            0 :          for (i = 1; i < mxml_get_line_number_end(mxml_find_node(c->pnseq, "RunSequence")); i++) {
    2780            0 :             pt = mxml_get_node_at_line(c->pnseq, i);
    2781            0 :             if (pt) {
    2782            0 :                if (equal_ustring(mxml_get_name(pt), "Subroutine")) {
    2783            0 :                   if (equal_ustring(mxml_get_attribute(pt, "name"), mxml_get_attribute(pn, "name"))) {
    2784              :                      // put routine end line on end stack
    2785            0 :                      seq.subroutine_end_line[seq.stack_index] = mxml_get_line_number_end(pt);
    2786              :                      // go to first line of subroutine
    2787            0 :                      seq.current_line_number = mxml_get_line_number_start(pt) + 1;
    2788              :                      // put parameter(s) on stack
    2789            0 :                      if (mxml_get_value(pn)) {
    2790              :                         char p[256];
    2791            0 :                         if (strchr(mxml_get_value(pn), '$')) // evaluate message string if $ present
    2792            0 :                            mstrlcpy(p, eval_var(seq, c, mxml_get_value(pn)).c_str(), sizeof(p));
    2793              :                         else
    2794            0 :                            mstrlcpy(p, mxml_get_value(pn), sizeof(p)); // treat message as string
    2795              : 
    2796            0 :                         mstrlcpy(seq.subroutine_param[seq.stack_index], p, 256);
    2797              :                      }
    2798              :                      // increment stack
    2799            0 :                      seq.stack_index++;
    2800            0 :                      break;
    2801              :                   }
    2802              :                }
    2803              :             }
    2804              :          }
    2805            0 :          if (i == mxml_get_line_number_end(mxml_find_node(c->pnseq, "RunSequence"))) {
    2806            0 :             seq_error(seq, c, msprintf("Subroutine '%s' not found", mxml_get_attribute(pn, "name")).c_str());
    2807              :          }
    2808              :       }
    2809              :    }
    2810              : 
    2811              :    /*---- <unknown> ----*/
    2812              :    else {
    2813            0 :       seq_error(seq, c, msprintf("Unknown statement \"%s\"", mxml_get_name(pn)).c_str());
    2814              :    }
    2815              : 
    2816              :    /* get steering parameters, since they might have been changed in between */
    2817              :    SEQUENCER seq1;
    2818            0 :    size = sizeof(seq1);
    2819            0 :    db_get_record(c->hDB, hKeySeq, &seq1, &size, 0);
    2820            0 :    seq.running = seq1.running;
    2821            0 :    seq.finished = seq1.finished;
    2822            0 :    seq.paused = seq1.paused;
    2823            0 :    seq.debug = seq1.debug;
    2824            0 :    seq.stop_after_run = seq1.stop_after_run;
    2825            0 :    mstrlcpy(seq.message, seq1.message, sizeof(seq.message));
    2826            0 :    memcpy(seq.next_filename, seq1.next_filename, sizeof(seq.next_filename));
    2827              : 
    2828              :    // in debugging mode, pause after each step
    2829            0 :    if (seq.debug && !skip_step)
    2830            0 :       seq.paused = TRUE;
    2831              : 
    2832              :    /* update current line number */
    2833            0 :    db_set_record(c->hDB, hKeySeq, &seq, sizeof(seq), 0);
    2834            0 : }
    2835              : 
    2836              : /*------------------------------------------------------------------*/
    2837              : 
    2838            0 : void init_sequencer(SEQUENCER& seq, SeqCon* c) {
    2839              :    int status;
    2840              :    HNDLE hKey;
    2841              : 
    2842            0 :    c->odbs = gOdb->Chdir(c->odb_path.c_str(), true); // create /Sequencer
    2843            0 :    assert(c->odbs);
    2844              : 
    2845            0 :    status = db_find_key(c->hDB, 0, c->odb_path.c_str(), &c->hSeq);
    2846            0 :    if (status != DB_SUCCESS) {
    2847            0 :       cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot find /Sequencer, db_find_key() status %d", status);
    2848            0 :       return;
    2849              :    }
    2850              : 
    2851            0 :    status = db_check_record(c->hDB, c->hSeq, "State", strcomb1(sequencer_str).c_str(), TRUE);
    2852            0 :    if (status == DB_STRUCT_MISMATCH) {
    2853            0 :       cm_msg(MERROR, "init_sequencer", "Sequencer error: mismatching /Sequencer/State structure, db_check_record() status %d", status);
    2854            0 :       return;
    2855              :    }
    2856              : 
    2857            0 :    status = db_find_key(c->hDB, c->hSeq, "State", &hKey);
    2858            0 :    if (status != DB_SUCCESS) {
    2859            0 :       cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot find /Sequencer/State, db_find_key() status %d", status);
    2860            0 :       return;
    2861              :    }
    2862              : 
    2863            0 :    int size = sizeof(seq);
    2864            0 :    status = db_get_record1(c->hDB, hKey, &seq, &size, 0, strcomb1(sequencer_str).c_str());
    2865            0 :    if (status != DB_SUCCESS) {
    2866            0 :       cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot get /Sequencer/State, db_get_record1() status %d", status);
    2867            0 :       return;
    2868              :    }
    2869              : 
    2870            0 :    if (strlen(seq.path) > 0 && seq.path[strlen(seq.path) - 1] != DIR_SEPARATOR) {
    2871            0 :       mstrlcat(seq.path, DIR_SEPARATOR_STR, sizeof(seq.path));
    2872              :    }
    2873              : 
    2874            0 :    if (seq.filename[0])
    2875            0 :       seq_open_file(seq.filename, seq, c);
    2876              : 
    2877            0 :    seq.transition_request = FALSE;
    2878              : 
    2879            0 :    db_set_record(c->hDB, hKey, &seq, sizeof(seq), 0);
    2880              : 
    2881            0 :    status = db_watch(c->hDB, hKey, seq_watch, c);
    2882            0 :    if (status != DB_SUCCESS) {
    2883            0 :       cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot watch /Sequencer/State, db_watch() status %d", status);
    2884            0 :       return;
    2885              :    }
    2886              : 
    2887            0 :    bool b = false;
    2888            0 :    c->odbs->RB("Command/Start script", &b, true);
    2889            0 :    b = false;
    2890            0 :    c->odbs->RB("Command/Stop immediately", &b, true);
    2891            0 :    b = false;
    2892            0 :    c->odbs->RB("Command/Stop after run", &b, true);
    2893            0 :    b = false;
    2894            0 :    c->odbs->RB("Command/Cancel stop after run", &b, true);
    2895            0 :    b = false;
    2896            0 :    c->odbs->RB("Command/Pause script", &b, true);
    2897            0 :    b = false;
    2898            0 :    c->odbs->RB("Command/Resume script", &b, true);
    2899            0 :    b = false;
    2900            0 :    c->odbs->RB("Command/Debug script", &b, true);
    2901            0 :    b = false;
    2902            0 :    c->odbs->RB("Command/Step over", &b, true);
    2903            0 :    b = false;
    2904            0 :    c->odbs->RB("Command/Load new file", &b, true);
    2905              : 
    2906            0 :    status = db_find_key(c->hDB, c->hSeq, "Command", &hKey);
    2907            0 :    if (status != DB_SUCCESS) {
    2908            0 :       cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot find /Sequencer/Command, db_find_key() status %d", status);
    2909            0 :       return;
    2910              :    }
    2911              : 
    2912            0 :    status = db_watch(c->hDB, hKey, seq_watch_command, c);
    2913            0 :    if (status != DB_SUCCESS) {
    2914            0 :       cm_msg(MERROR, "init_sequencer", "Sequencer error: Cannot watch /Sequencer/Command, db_watch() status %d", status);
    2915            0 :       return;
    2916              :    }
    2917              : }
    2918              : 
    2919              : /*------------------------------------------------------------------*/
    2920              : 
    2921            0 : int main(int argc, const char *argv[]) {
    2922            0 :    int daemon = FALSE;
    2923              :    int status, ch;
    2924              :    char midas_hostname[256];
    2925              :    char midas_expt[256];
    2926            0 :    std::string seq_name = "";
    2927              : 
    2928            0 :    setbuf(stdout, NULL);
    2929            0 :    setbuf(stderr, NULL);
    2930              : #ifdef SIGPIPE
    2931              :    /* avoid getting killed by "Broken pipe" signals */
    2932            0 :    signal(SIGPIPE, SIG_IGN);
    2933              : #endif
    2934              : 
    2935              :    /* get default from environment */
    2936            0 :    cm_get_environment(midas_hostname, sizeof(midas_hostname), midas_expt, sizeof(midas_expt));
    2937              : 
    2938              :    /* parse command line parameters */
    2939            0 :    for (int i = 1; i < argc; i++) {
    2940            0 :       if (argv[i][0] == '-' && argv[i][1] == 'D') {
    2941            0 :          daemon = TRUE;
    2942            0 :       } else if (argv[i][0] == '-') {
    2943            0 :          if (i + 1 >= argc || argv[i + 1][0] == '-')
    2944            0 :             goto usage;
    2945            0 :          if (argv[i][1] == 'h')
    2946            0 :             mstrlcpy(midas_hostname, argv[++i], sizeof(midas_hostname));
    2947            0 :          else if (argv[i][1] == 'e')
    2948            0 :             mstrlcpy(midas_expt, argv[++i], sizeof(midas_hostname));
    2949            0 :          else if (argv[i][1] == 'c')
    2950            0 :             seq_name = argv[++i];
    2951              :       } else {
    2952            0 :          usage:
    2953            0 :          printf("usage: %s [-h Hostname[:port]] [-e Experiment] [-c Name] [-D]\n\n", argv[0]);
    2954            0 :          printf("       -e experiment to connect to\n");
    2955            0 :          printf("       -c Name of additional sequencer, i.e. \'Test\'\n");
    2956            0 :          printf("       -h connect to midas server (mserver) on given host\n");
    2957            0 :          printf("       -D become a daemon\n");
    2958            0 :          return 0;
    2959              :       }
    2960              :    }
    2961              : 
    2962            0 :    if (daemon) {
    2963            0 :       printf("Becoming a daemon...\n");
    2964            0 :       ss_daemon_init(FALSE);
    2965              :    }
    2966              : 
    2967            0 :    std::string prg_name = "Sequencer";
    2968            0 :    std::string odb_path = "Sequencer";
    2969              :    
    2970            0 :    if (!seq_name.empty()) {
    2971            0 :       prg_name = std::string("Sequencer") + seq_name; // sequencer program name
    2972            0 :       odb_path = std::string("Sequencer") + seq_name; // sequencer ODB path
    2973              :    }
    2974              : 
    2975              : #ifdef OS_LINUX
    2976            0 :    std::string pid_filename;
    2977            0 :    pid_filename += "/var/run/";
    2978            0 :    pid_filename += prg_name;
    2979            0 :    pid_filename += ".pid";
    2980              :    /* write PID file */
    2981            0 :    FILE *f = fopen(pid_filename.c_str(), "w");
    2982            0 :    if (f != NULL) {
    2983            0 :       fprintf(f, "%d", ss_getpid());
    2984            0 :       fclose(f);
    2985              :    }
    2986              : #endif
    2987              : 
    2988              :    /*---- connect to experiment ----*/
    2989            0 :    status = cm_connect_experiment1(midas_hostname, midas_expt, prg_name.c_str(), NULL, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
    2990            0 :    if (status == CM_WRONG_PASSWORD)
    2991            0 :       return 1;
    2992            0 :    else if (status == DB_INVALID_HANDLE) {
    2993            0 :       std::string s = cm_get_error(status);
    2994            0 :       puts(s.c_str());
    2995            0 :    } else if (status != CM_SUCCESS) {
    2996            0 :       std::string s = cm_get_error(status);
    2997            0 :       puts(s.c_str());
    2998            0 :       return 1;
    2999            0 :    }
    3000              : 
    3001              :    /* check if sequencer already running */
    3002            0 :    status = cm_exist(prg_name.c_str(), TRUE);
    3003            0 :    if (status == CM_SUCCESS) {
    3004            0 :       printf("%s runs already.\n", prg_name.c_str());
    3005            0 :       cm_disconnect_experiment();
    3006            0 :       return 1;
    3007              :    }
    3008              : 
    3009              :    HNDLE hDB;
    3010            0 :    cm_get_experiment_database(&hDB, NULL);
    3011            0 :    gOdb = MakeMidasOdb(hDB);
    3012              : 
    3013              :    SEQUENCER seq;
    3014            0 :    SeqCon c;
    3015              : 
    3016            0 :    c.seqp = &seq;
    3017            0 :    c.hDB = hDB;
    3018            0 :    c.seq_name = seq_name;
    3019            0 :    c.prg_name = prg_name;
    3020            0 :    c.odb_path = odb_path;
    3021              : 
    3022            0 :    init_sequencer(seq, &c);
    3023              : 
    3024            0 :    if (seq_name.empty()) {
    3025            0 :       printf("Sequencer started. Stop with \"!\"\n");
    3026              :    } else {
    3027            0 :       printf("%s started.\n", prg_name.c_str());
    3028            0 :       printf("Set ODB String \"/Alias/Sequencer%s\" to URL \"?cmd=sequencer&seq_name=%s\"\n", seq_name.c_str(), seq_name.c_str()); // FIXME: seq_name should be URL-encoded! K.O. Aug 2023
    3029            0 :       printf("Stop with \"!\"\n");
    3030              :    }
    3031              : 
    3032              :    // if any commands are active, process them now
    3033            0 :    seq_watch_command(hDB, 0, 0, &c);
    3034              : 
    3035              :    /* initialize ss_getchar */
    3036            0 :    ss_getchar(0);
    3037              : 
    3038              :    /* main loop */
    3039              :    do {
    3040              :       try {
    3041            0 :          sequencer(seq, &c);
    3042            0 :       } catch (std::string &msg) {
    3043            0 :          seq_error(seq, &c, msg.c_str());
    3044            0 :       } catch (const char *msg) {
    3045            0 :          seq_error(seq, &c, msg);
    3046            0 :       }
    3047              : 
    3048            0 :       status = cm_yield(0);
    3049              : 
    3050            0 :       ch = 0;
    3051            0 :       while (ss_kbhit()) {
    3052            0 :          ch = ss_getchar(0);
    3053            0 :          if (ch == -1)
    3054            0 :             ch = getchar();
    3055              : 
    3056            0 :          if ((char) ch == '!')
    3057            0 :             break;
    3058              :       }
    3059              : 
    3060            0 :    } while (status != RPC_SHUTDOWN && ch != '!');
    3061              : 
    3062              :    /* reset terminal */
    3063            0 :    ss_getchar(TRUE);
    3064              : 
    3065              :    /* close network connection to server */
    3066            0 :    cm_disconnect_experiment();
    3067              : 
    3068            0 :    return 0;
    3069            0 : }
    3070              : 
    3071              : /*------------------------------------------------------------------*/
    3072              : 
    3073              : /**dox***************************************************************/
    3074              : /** @} */ /* end of alfunctioncode */
    3075              : 
    3076              : /* emacs
    3077              :  * Local Variables:
    3078              :  * tab-width: 8
    3079              :  * c-basic-offset: 3
    3080              :  * indent-tabs-mode: nil
    3081              :  * End:
    3082              :  */
        

Generated by: LCOV version 2.0-1