diff options
| -rw-r--r-- | server/src/daemon.cc | 2 | ||||
| -rw-r--r-- | server/src/pracrod.cc | 25 | ||||
| -rw-r--r-- | server/src/saxparser.h | 1 | ||||
| -rw-r--r-- | server/src/server.cc | 103 | ||||
| -rw-r--r-- | server/src/tcpsocket.cc | 178 | ||||
| -rw-r--r-- | server/src/tcpsocket.h | 7 | ||||
| -rw-r--r-- | server/src/transactionparser.cc | 42 | ||||
| -rw-r--r-- | server/src/transactionparser.h | 6 | ||||
| -rw-r--r-- | server/xml/macros/example.xml | 93 | 
9 files changed, 343 insertions, 114 deletions
| diff --git a/server/src/daemon.cc b/server/src/daemon.cc index 655a234..6aed66e 100644 --- a/server/src/daemon.cc +++ b/server/src/daemon.cc @@ -153,7 +153,7 @@ int Daemon::run(const char *user, const char* group, bool detach)    setsid();    signal (SIGTERM, SIG_IGN); -  signal (SIGHUP, SIG_IGN); +  //  signal (SIGHUP, SIG_IGN);    if(detach) signal (SIGINT, SIG_IGN); // Don't disable Ctrl+c when running in foreground.    return daemon_main(); diff --git a/server/src/pracrod.cc b/server/src/pracrod.cc index 6ee1817..6ca21db 100644 --- a/server/src/pracrod.cc +++ b/server/src/pracrod.cc @@ -33,6 +33,9 @@  #include <sys/types.h>  #include <unistd.h> +// For waitpid +#include <sys/wait.h> +  // For signal  #include <signal.h> @@ -85,6 +88,24 @@ static const char usage_str[] =  ConfigurationParser *configparser = NULL; +bool pracro_is_running = true; + +void ctrl_c(int) +{ +  //  printf("Ctrl+c\n"); +  pracro_is_running = false; +} + +void childwait(int) +{ +  //  printf("childwait\n"); + +  pid_t pid; +  while((pid = waitpid(-1, NULL, WNOHANG)) > 0) { +    //    printf("\tOne down!\n"); +  } +} +  void reload(int)  {    int port; @@ -183,6 +204,10 @@ int main(int argc, char *argv[])      group = strdup(Conf::server_group.c_str());    } +  signal(SIGHUP, reload); +  signal(SIGCLD, childwait); +  if(foreground) signal (SIGINT, ctrl_c); +    PracroDaemon daemon;    daemon.run(user, group, !foreground); diff --git a/server/src/saxparser.h b/server/src/saxparser.h index 2ee4f43..67a86b7 100644 --- a/server/src/saxparser.h +++ b/server/src/saxparser.h @@ -47,7 +47,6 @@ public:  protected:    virtual int readData(char *data, size_t size) { return 0; } -private:    XML_Parser p;  }; diff --git a/server/src/server.cc b/server/src/server.cc index a419181..d2921b8 100644 --- a/server/src/server.cc +++ b/server/src/server.cc @@ -52,6 +52,8 @@  static std::string error_box(std::string message)  {    std::string errorbox = +    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +    "<pracro version=\"1.0\">\n"      "  <course name=\"error\">\n"      "    <macro name=\"error\">\n"      "      <window caption=\"ERROR!\" height=\"300\" layout=\"vbox\" name=\"error\" width=\"480\">\n" @@ -59,7 +61,8 @@ static std::string error_box(std::string message)      "        <button action=\"cancel\" caption=\"Luk\" name=\"cancel\"/>\n"      "      </window>\n"      "    </macro>\n" -    "  </course>\n"; +    "  </course>\n" +    "</pracro>\n";    return errorbox;  } @@ -69,16 +72,13 @@ public:    : Exception("Macro " + r.macro + " not found in course " + r.course) {}  }; -static void connection(TCPSocket &socket) +static std::string handleTransaction(Transaction &transaction)  { -  socket.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); -  socket.write("<pracro version=\"1.0\">\n"); +  std::string answer; +  answer += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; +  answer += "<pracro version=\"1.0\">\n";    try { -    Transaction transaction; -    TransactionParser parser(socket, transaction); -    parser.parse(); -      Database db;      // @@ -112,8 +112,6 @@ static void connection(TCPSocket &socket)      while(i != transaction.requests.end()) {        Request &request = *i; -      std::string answer; -        printf("Handling request - macro: %s, course: %s\n",               request.macro.c_str(), request.course.c_str()); @@ -201,26 +199,72 @@ static void connection(TCPSocket &socket)        answer += "  </course>\n"; -      socket.write(answer);        i++;      } + +    answer += "</pracro>\n"; +    } catch( PGSTD::runtime_error &e ) { -    socket.write(error_box(xml_encode(std::string("PostgreSQL server error:\n") + e.what()))); +    answer = error_box(xml_encode(std::string("PostgreSQL server error:\n") + e.what()));    } catch( std::exception &e ) { -    socket.write(error_box(xml_encode(e.what()))); +    answer = error_box(xml_encode(e.what()));    } -  socket.write("</pracro>\n"); +  return answer;  } +static void handleConnection(TCPSocket *socket) +{ +  size_t bufsize = 256; +  int size; +  char buf[256]; +  memset(buf, 0, bufsize); +   +  Transaction *transaction = NULL; +  TransactionParser *parser = NULL; +   +  while((size = socket->read(buf, bufsize)) > 0) { +     +    if(transaction == NULL) { +      transaction = new Transaction(); +      parser = new TransactionParser(transaction); +    } + +    printf("Got %d bytes in read loop\n", size); +    if(parser->parse(buf)) { +      socket->write(handleTransaction(*transaction)); + +      delete transaction; +      transaction = NULL; + +      delete parser; +      parser = NULL; +    } +    memset(buf, 0, bufsize); +  } + +  if(transaction) { +    delete transaction; +    transaction = NULL; + +    delete parser; +    parser = NULL; +  } + +  printf("Out of read loop!\n"); +} + +//#define NON_FORKING +#include <sys/socket.h> +extern bool pracro_is_running;  void server()  {    port_t port = Conf::server_port;    TCPSocket *socket = NULL;    try { -    socket = new TCPSocket(); +    socket = new TCPSocket("Listen socket");      socket->listen(port);    } catch (Exception &e) {      fprintf(stderr, "Error during parsing:\n%s\n", @@ -230,7 +274,7 @@ void server()      return;    } -  while(socket->connected()) { +  while(pracro_is_running && socket->connected()) {      { // Reload if new port is assigned.        int old_port = port; @@ -239,41 +283,44 @@ void server()        if(port != old_port) {          // Start listening on the new port          delete socket; -        socket = new TCPSocket(); +        socket = new TCPSocket("Listen socket (reloaded)");          socket->listen(port);        }      } -    TCPSocket child = socket->accept(); -    if(child.connected()) { -      //socket->disconnect(); -      connection(child); -      //delete socket; +    TCPSocket *child = socket->accept(); +    if(child) { -      /* +#ifndef NON_FORKING        switch(fork()) {        case -1: // error          fprintf(stderr, "Could not fork: %s\n", strerror(errno));          break;        case 0: // child -        socket->disconnect(); -        connection(child);          delete socket; +#endif/*NON_FORKING*/ +        handleConnection(child); +        delete child; +#ifndef NON_FORKING          return;        default: // parent -        child.disconnect(); +        delete child;          break;        } -      */ +#endif/*NON_FORKING*/ +      }    } +  //socket->shutdown();    delete socket; -  fprintf(stderr, "Oups... dropped out of the main loop\n"); + +  printf("Server gracefully shut down.\n");  } +  #ifdef TEST_SERVER  char request[] =  diff --git a/server/src/tcpsocket.cc b/server/src/tcpsocket.cc index 4bb7a4b..b6049c7 100644 --- a/server/src/tcpsocket.cc +++ b/server/src/tcpsocket.cc @@ -27,6 +27,9 @@  #include "tcpsocket.h"  #include "debug.h" +#include <config.h> + +//#define WITH_DEBUG  // for gethostbyname  #include <netdb.h> @@ -74,17 +77,28 @@  #include <unistd.h>  #include <fcntl.h> -TCPSocket::TCPSocket() +TCPSocket::TCPSocket(std::string name, int sock)    throw(TCPSocketException)  { -  if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { -    throw TCPSocketException(strerror(errno)); +  this->name = name; +  if(sock == -1) { +    if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { +      throw TCPSocketException(strerror(errno)); +    }    }    isconnected = false; +  this->sock = sock; + +#ifdef WITH_DEBUG +   printf("TCPSocket %s: %p %d (%d)\n", name.c_str(), this, sock, getpid()); +#endif/*WITH_DEBUG*/  }  TCPSocket::~TCPSocket()  { +#ifdef WITH_DEBUG +  printf("~TCPSocket %s: %p %d (%d)\n", name.c_str(), this, sock, getpid()); +#endif/*WITH_DEBUG*/    disconnect();  } @@ -94,13 +108,8 @@ void TCPSocket::listen(unsigned short int port)    throw(TCPListenException)  { -  if(sock == -1) { -    throw TCPListenException("Socket not initialized."); -  } - -  if(isconnected) { -    throw TCPListenException("Socket already connected."); -  } +  if(sock == -1) throw TCPListenException("Socket not initialized."); +  if(isconnected) throw TCPListenException("Socket already connected.");    struct sockaddr_in socketaddr;    memset((char *) &socketaddr, sizeof(socketaddr), 0); @@ -119,35 +128,52 @@ void TCPSocket::listen(unsigned short int port)    isconnected = true;  } - +/** + ** + ** Accept connections and block until one gets in. + ** Return the new connection on incoming. + ** It throws exceptions if an error occurres. + ** On interrupts, it returns NULL + ** + **/  static int _accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)  {return accept(sockfd, addr, addrlen);} -TCPSocket TCPSocket::accept() +TCPSocket *TCPSocket::accept()    throw(TCPAcceptException)  { -  TCPSocket child; - -  if(sock == -1) { -    throw TCPAcceptException("Socket not initialized."); -  } - -  if(!isconnected) { -    throw TCPAcceptException("Socket not connected."); -  } +  if(sock == -1) throw TCPAcceptException("Socket not initialized."); +  if(!isconnected) throw TCPAcceptException("Socket not connected.");    // accept new connection and get its connection descriptor    struct sockaddr_in ssocketaddr;    int csalen = sizeof(ssocketaddr); -  child.disconnect(); // We need to close the existing socket -  child.sock = _accept(sock, (struct sockaddr*)&ssocketaddr, (socklen_t*)&csalen); - -  if (child.sock == -1) { -    throw TCPAcceptException(std::string("accept failed - ") + strerror(errno)); +  // Select +  fd_set fset; +  int ret; +  FD_ZERO(&fset); +  FD_SET(sock, &fset); +  if( (ret = select (sock+1, &fset, NULL, NULL, NULL)) < 0) {  +    if(errno == EINTR) { +      printf("Accept got interrupt!\n"); +      return NULL; // a signal caused select to return. That is OK with me +    } else { +      throw TCPAcceptException("Select on socket failed."); +    } +  } +  if(FD_ISSET(sock, &fset)) { +    int csock = _accept(sock, (struct sockaddr*)&ssocketaddr, (socklen_t*)&csalen); +    TCPSocket *child = new TCPSocket(name + "-child", csock); +     +    if (child->sock == -1) { +      throw TCPAcceptException(std::string("accept failed - ") + strerror(errno)); +    } +    child->isconnected = true; +    return child; +  } else { +    printf("Accept returned with no socket - This should not happen!\n"); +    return NULL;    } - -  child.isconnected = true; -  return child;  } @@ -155,11 +181,8 @@ static int _connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addr  {return connect(sockfd, serv_addr, addrlen);}  void TCPSocket::connect(std::string addr, unsigned short int port)    throw(TCPConnectException) -    { -  if(isconnected) { -    throw TCPConnectException("Socket already connected.", "", ""); -  } +  if(isconnected) throw TCPConnectException("Socket already connected.", "", "");  #ifndef BYPASS_STATICALLOCATIONS    // Do DNS lookup @@ -199,7 +222,13 @@ void TCPSocket::connect(std::string addr, unsigned short int port)  void TCPSocket::disconnect()  {    if(sock != -1) { -    close(sock); +#ifdef WITH_DEBUG +    printf("Closing TCPSocket %s: %p %d (%d)\n", name.c_str(), this, sock, getpid()); +#endif/*WITH_DEBUG*/ +    int ret = close(sock); +    if(ret == -1) { +      perror(name.c_str()); +    }      sock = -1;    }    isconnected = false; @@ -210,41 +239,69 @@ bool TCPSocket::connected()    return sock != -1 && isconnected;  } -int TCPSocket::read(char *buf, int size) + + +/** + ** + ** Read read a number of bytes from the network. + ** It returns the number of bytes read. + ** It throws exceptions if an error occurres. + ** On interrupts, it returns -1 + ** + **/ +ssize_t _read(int fd, void *buf, size_t count) { return read(fd, buf, count); } +int TCPSocket::read(char *buf, int size, long timeout)    throw(TCPReadException)  {    int res = 0; -  if(sock == -1) { -    throw TCPReadException("Socket not initialized."); -  } - -  if(!isconnected) { -    throw TCPReadException("Socket is not connected."); -  } - -  /* -  if( (res = recv(sock, buf, size, MSG_WAITALL)) == -1 ) { -    throw TCPReadException(strerror(errno)); -  } -  */ +  if(sock == -1) throw TCPReadException("Socket not initialized."); +  if(!isconnected) throw TCPReadException("Socket is not connected."); + +  // Select +  struct timeval tv; +  tv.tv_sec = 0; +  tv.tv_usec = timeout; + +  struct timeval *ptv = NULL; +  if(timeout >= 0) ptv = &tv; + +  fd_set fset; +  int ret; +  FD_ZERO(&fset); +  FD_SET(sock, &fset); +  ret = select (sock+1, &fset, NULL, NULL, ptv); +  switch(ret) { +  case -1: +    if(errno == EINTR) { +      printf("EINTR - got interrupt\n"); +      return -1; // a signal caused select to return. That is OK with me +    } else { +      throw TCPReadException("Select on socket (read) failed."); +    } +    break; -  // Wait until something is ready to be read ( peek'a'loop ). -  errno = EAGAIN; -  while( recv(sock, buf, 1, MSG_PEEK) == -1 && errno == EAGAIN) { -    usleep(10); -  } +  case 0: +    // timeout +    printf("Timeout\n"); +    break; -  // Now read it -  if( (res = recv(sock, buf, size, MSG_DONTWAIT)) == -1 ) { -    throw TCPReadException(strerror(errno)); +  default: +    if(FD_ISSET(sock, &fset)) { +      //      res = recv(sock, buf, size, MSG_DONTWAIT); +      if( (res = _read(sock, buf, size)) == -1 ) { +        throw TCPReadException(strerror(errno)); +      } +    } else { +      printf("FD_ISSET failed (timeout?)\n"); +      return 0; +    }    } -  //  fwrite(buf, size, 1, stdout); fflush(stdout); -    return res;  } +ssize_t _write(int fd, const void *buf, size_t count) { return write(fd, buf, count); }   int TCPSocket::write(char *data, int size)    throw(TCPWriteException)  { @@ -257,10 +314,13 @@ int TCPSocket::write(char *data, int size)    }    int res; -  if( (res = send(sock, data, size, MSG_WAITALL)) == -1 ) { +  //  if( (res = send(sock, data, size, MSG_WAITALL)) == -1 ) { +  if( (res = _write(sock, data, size)) == -1 ) {      throw TCPWriteException(strerror(errno));    } +  printf("Outputted %d byes\n", res); +    return res;  } diff --git a/server/src/tcpsocket.h b/server/src/tcpsocket.h index 393d40b..4771d10 100644 --- a/server/src/tcpsocket.h +++ b/server/src/tcpsocket.h @@ -103,7 +103,7 @@ public:    /**     * Constructor. Creates a new tcp socket.     */ -  TCPSocket() throw(TCPSocketException); +  TCPSocket(std::string name = "", int sock = -1) throw(TCPSocketException);    /**     * Destructor. Closes the tcp socket. @@ -123,7 +123,7 @@ public:     * Multiple accepts can be made on the same listening socket.     * @return A connected TCPSocket ready to communicate.     */ -  TCPSocket accept() throw(TCPAcceptException); +  TCPSocket *accept() throw(TCPAcceptException);    /**     * Connects to a host for data transmission. @@ -149,7 +149,7 @@ public:     * @param size The maximum number of bytes to read in (the size of the buffer).     * @return The actual number of bytes read.     */ -  int read(char *buf, int size) throw(TCPReadException); +  int read(char *buf, int size, long timeout = -1) throw(TCPReadException);    /**     * Writes bytes from a buffer to the socket. @@ -175,6 +175,7 @@ public:  private:    bool isconnected;    int sock; +  std::string name;  }; diff --git a/server/src/transactionparser.cc b/server/src/transactionparser.cc index 0e64944..3d3024c 100644 --- a/server/src/transactionparser.cc +++ b/server/src/transactionparser.cc @@ -33,13 +33,16 @@  #include <string>  #include <map> -TransactionParser::TransactionParser(TCPSocket &socket, Transaction &transaction) +TransactionParser::TransactionParser(Transaction *transaction)  { -  this->transaction = &transaction; -  this->socket = &socket; +  this->transaction = transaction;    done = false;  } +TransactionParser::~TransactionParser() +{ +} +  void TransactionParser::startTag(std::string name, std::map< std::string, std::string> attributes)  {    if(name == "pracro") { @@ -74,8 +77,39 @@ void TransactionParser::endTag(std::string name)  int TransactionParser::readData(char *data, size_t size)  { +  printf("readData is not uasble with transaction parser!.\nUse parse(std::string) instead.\n"); +  return 0; +/*    if(done) return 0; -  return socket->read(data, size); + +  int ret; + +  //  while((ret = socket->read(data, size, 1000)) == 0) { } +   +  if(ret == -1) { +    printf("Transaction parser was interrupted.\n"); +    return 0; +  } +   +  return ret; +*/ +} + +bool TransactionParser::parse(std::string data) +{ +  if(! XML_Parse(p, (char*)data.c_str(), data.size(), false) ) { +    parseError((char*)data.c_str(), data.size(), XML_ErrorString(XML_GetErrorCode(p)), (int)XML_GetCurrentLineNumber(p)); +    return false; +  } + +  if(done) { +    if(! XML_Parse(p, "", 0, true) ) { +      parseError("", 0, XML_ErrorString(XML_GetErrorCode(p)), (int)XML_GetCurrentLineNumber(p)); +      return false; +    } +  } + +  return done;  }  void TransactionParser::parseError(char *buf, size_t len, std::string error, int lineno) diff --git a/server/src/transactionparser.h b/server/src/transactionparser.h index b64d68a..3f477c9 100644 --- a/server/src/transactionparser.h +++ b/server/src/transactionparser.h @@ -33,19 +33,21 @@  class TransactionParser : public SAXParser {  public: -  TransactionParser(TCPSocket &socket, Transaction &transaction); +  TransactionParser(Transaction *transaction); +  ~TransactionParser();    void startTag(std::string name, std::map< std::string, std::string> attributes);    void endTag(std::string name);    void parseError(char *buf, size_t len, std::string error, int lineno); +  bool parse(std::string data); +  protected:    int readData(char *data, size_t size);  private:    Transaction *transaction; -  TCPSocket *socket;    bool done;  }; diff --git a/server/xml/macros/example.xml b/server/xml/macros/example.xml index eafcf33..97865ca 100644 --- a/server/xml/macros/example.xml +++ b/server/xml/macros/example.xml @@ -27,8 +27,8 @@        return value, timestamp      </map>    </maps> -  <luaprograms> -    <luaprogram name="theanswer"> +  <scripts> +    <script language="lua" name="theanswer">        sphere = tonumber(getValue('sphere'))        axis = tonumber(getValue('axis'))        if( sphere == axis ) @@ -38,37 +38,98 @@        else          return false        end -    </luaprogram> -  </luaprograms> +    </script> +  </scripts>    <window name="mainwindow"            caption="Example Window" -          width="300" +          width="400"            height="500"            layout="vbox">      <frame name="linse_frame" caption="Linser:" layout="vbox">        <frame name="linse_framea" layout="hbox"> +        <icon src="righteye"/>          <label name="a" width="100" caption="Akse rotation:"/>          <lineedit name="axis" regexp="[24]*" map="axis" lua="theanswer" value="244"/>        </frame>        <frame name="linse_frameb" layout="hbox"> +        <icon src="lefteye"/>          <label name="b" width="100" caption="Sphere:"/> -        <lineedit name="sphere" regexp="[24]*" lua="theanswer" value="42"/>        </frame>        <frame name="linse_framec" layout="hbox">          <label name="c" width="100" caption="Cyl:"/> -        <lineedit name="cyl" regexp="[24]*" map="cyl" value=""/> +        <lineedit name="cyl" regexp=".*" map="cyl" value=""/>        </frame>      </frame>      <frame name="linse_frame" caption="FlereLinser:" layout="vbox"> -      <multilist name="multilisttest" layout="hbox"> -        <items layout="hbox"> -          <item value="Pillemigselvinavlen"/> -          <item value="Øffegøj"/> -        </items> -        <input> -          <label name="c" width="100" caption="Cyl2:"/> -          <lineedit name="cyl2" regexp="[24]*" map="cyl" value=""/> -        </input> +      <multilist name="multilisttest" value="" layout="vbox" format="Sphere: ${spherecombo}, Cyl: ${multcyl}."> +        <frame name="linse_frameb" layout="hbox"> +          <label name="b" width="100" caption="Sphere:"/> +	  <combobox name="spherecombo" width="100" value="val_lad" type="search"> +	    <item caption="sap" value="val_sap"/> +	    <item caption="sat" value="val_sat"/> +	    <item caption="sad" value="val_sad"/> +	    <item caption="rat" value="val_rat"/> +	    <item caption="rap" value="val_rap"/> +	    <item caption="ram" value="val_ram"/> +	    <item caption="rag" value="val_rag"/> +	    <item caption="nap" value="val_nap"/> +	    <item caption="Nat" value="val_Nat"/> +	    <item caption="mat" value="val_mat"/> +	    <item caption="map" value="val_map"/> +	    <item caption="mad" value="val_mad"/> +	    <item caption="lap" value="val_lap"/> +	    <item caption="lag" value="val_lag"/> +	    <item caption="lad" value="val_lad"/> +	    <item caption="fat" value="val_fat"/> +	    <item caption="fan" value="val_fan"/> +	    <item caption="fad" value="val_fad"/> +	    <item caption="fin" value="val_fin"/> +	    <item caption="fit" value="val_fit"/> +	    <item caption="lid" value="val_lid"/> +	    <item caption="lip" value="val_lip"/> +	    <item caption="lit" value="val_lit"/> +	    <item caption="mid" value="val_mid"/> +	    <item caption="mit" value="val_mit"/> +	    <item caption="nit" value="val_nit"/> +	    <item caption="nip" value="val_nip"/> +	    <item caption="rid" value="val_rid"/> +	    <item caption="rig" value="val_rig"/> +	    <item caption="rim" value="val_rim"/> +	    <item caption="rip" value="val_rip"/> +	    <item caption="Sid" value="val_Sid"/> +	    <item caption="sin" value="val_sin"/> +	    <item caption="sip" value="val_sip"/> +	    <item caption="log" value="val_log"/> +	    <item caption="mom" value="val_mom"/> +	    <item caption="mop" value="val_mop"/> +	    <item caption="nod" value="val_nod"/> +	    <item caption="rod" value="val_rod"/> +	    <item caption="Ron" value="val_Ron"/> +	    <item caption="rot" value="val_rot"/> +	    <item caption="sod" value="val_sod"/> +	    <item caption="fun" value="val_fun"/> +	    <item caption="mud" value="val_mud"/> +	    <item caption="mum" value="val_mum"/> +	    <item caption="nut" value="val_nut"/> +	    <item caption="rug" value="val_rug"/> +	    <item caption="rut" value="val_rut"/> +	    <item caption="sum" value="val_sum"/> +	    <item caption="sun" value="val_sun"/> +	    <item caption="fed" value="val_fed"/> +	    <item caption="led" value="val_led"/> +	    <item caption="leg" value="val_leg"/> +	    <item caption="met" value="val_met"/> +	    <item caption="Ned" value="val_Ned"/> +	    <item caption="net" value="val_net"/> +	    <item caption="bag" value="val_bag"/> +	    <item caption="bad" value="val_bad"/> +	    <item caption="bam" value="val_bam"/> +	  </combobox> +        </frame> +        <frame name="linse_framec" layout="hbox"> +          <label name="c" width="100" caption="Cyl:"/> +          <lineedit name="multcyl" width="100" regexp=".*" value=""/> +        </frame>        </multilist>      </frame>      <frame name="buttons" layout="hbox"> | 
