Nicolae Brinza has proposed merging lp:~nbrinza/zorba/parse-fragment into 
lp:zorba.

Requested reviews:
  Matthias Brantner (matthias-brantner)
  Chris Hillery (ceejatec)
Related bugs:
  Bug #1016606 in Zorba: "xml:parse unable to parse content containing a 
DOCTYPE"
  https://bugs.launchpad.net/zorba/+bug/1016606
  Bug #1023170 in Zorba: "Segfault in xml:parse"
  https://bugs.launchpad.net/zorba/+bug/1023170
  Bug #1024033 in Zorba: "segfault in parse-xml:parse()"
  https://bugs.launchpad.net/zorba/+bug/1024033
  Bug #1027270 in Zorba: "xml:parse() - infinite loop"
  https://bugs.launchpad.net/zorba/+bug/1027270

For more details, see:
https://code.launchpad.net/~nbrinza/zorba/parse-fragment/+merge/117814

Fixes for bugs #1023170, #1024033, #1027270
-- 
https://code.launchpad.net/~nbrinza/zorba/parse-fragment/+merge/117814
Your team Zorba Coders is subscribed to branch lp:zorba.
=== modified file 'src/runtime/parsing_and_serializing/fragment_istream.h'
--- src/runtime/parsing_and_serializing/fragment_istream.h	2012-07-24 08:48:48 +0000
+++ src/runtime/parsing_and_serializing/fragment_istream.h	2012-08-01 22:40:26 +0000
@@ -30,8 +30,7 @@
 class FragmentIStream : public std::istream
 {
 public:
-  static const unsigned int BUFFER_SIZE = 4096;
-  static const unsigned int LOOKAHEAD_BYTES = 3; // lookahead fetching is implemented, but currently not used
+  static const unsigned int DEFAULT_BUFFER_SIZE = 4096;
   static const unsigned int PARSED_NODES_BATCH_SIZE = 1024;
   
   // names of these states are orientative
@@ -44,7 +43,8 @@
 public:
   std::istringstream* theIss;
   std::istream* theStream;
-  char* theBuffer;
+  StreamReleaser theStreamReleaser;
+  std::vector<char> theBuffer;
   unsigned long bytes_in_buffer;
   unsigned long current_offset;
   int current_element_depth;
@@ -64,7 +64,7 @@
     std::istream(NULL),
     theIss(NULL),
     theStream(NULL),
-    theBuffer(NULL),
+    theStreamReleaser(nullptr),
     bytes_in_buffer(0),
     current_offset(0),
     current_element_depth(0),
@@ -83,18 +83,30 @@
   {
     return reached_eof && current_offset >= bytes_in_buffer;
   }
+  
+  StreamReleaser getStreamReleaser()
+  {
+    return theStreamReleaser;
+  }
+  
+  void setStreamReleaser(StreamReleaser aReleaser)
+  {
+    theStreamReleaser = aReleaser;
+  }
 
   void reset()
   {
-    if (theBuffer)
-    {
-      delete[] theBuffer;
-    }
+    theBuffer.clear();
 
     if (theIss)
     {
       delete theIss;
     }
+    
+    if (theStreamReleaser)
+    {
+      theStreamReleaser(theStream);
+    }
 
     if (ctxt)
     {
@@ -104,7 +116,6 @@
 
     theIss = NULL;
     theStream = NULL;
-    theBuffer = NULL;
     bytes_in_buffer = 0;
     current_offset = 0;
     current_element_depth = 0;
@@ -117,7 +128,7 @@
     children = NULL;
     only_one_doc_node = false;
   }
-
+  
   virtual ~FragmentIStream()
   {
     reset();

=== modified file 'src/runtime/parsing_and_serializing/parse_fragment_impl.cpp'
--- src/runtime/parsing_and_serializing/parse_fragment_impl.cpp	2012-07-24 08:48:48 +0000
+++ src/runtime/parsing_and_serializing/parse_fragment_impl.cpp	2012-08-01 22:40:26 +0000
@@ -197,6 +197,8 @@
     if (result->isStreamable())
     {
       state->theFragmentStream.theStream = &result->getStream();
+      state->theFragmentStream.setStreamReleaser(result->getStreamReleaser());
+      result->setStreamReleaser(nullptr);
     }
     else
     {

=== modified file 'src/store/naive/loader_dtd.cpp'
--- src/store/naive/loader_dtd.cpp	2012-07-24 08:48:48 +0000
+++ src/store/naive/loader_dtd.cpp	2012-08-01 22:40:26 +0000
@@ -153,13 +153,13 @@
 {
   if (theFragmentStream->ctxt->input->length > 0 && theFragmentStream->current_offset < theFragmentStream->bytes_in_buffer)
   {
-    memmove(theFragmentStream->theBuffer, theFragmentStream->theBuffer + theFragmentStream->current_offset,
+    memmove(&theFragmentStream->theBuffer[0], &theFragmentStream->theBuffer[0] + theFragmentStream->current_offset,
             theFragmentStream->bytes_in_buffer - theFragmentStream->current_offset);
   }
   theFragmentStream->bytes_in_buffer -= theFragmentStream->current_offset;
 
-  std::streamsize numChars = readPacket(*theFragmentStream->theStream, theFragmentStream->theBuffer + theFragmentStream->bytes_in_buffer,
-                                         FragmentIStream::BUFFER_SIZE+FragmentIStream::LOOKAHEAD_BYTES - theFragmentStream->bytes_in_buffer);
+  std::streamsize numChars = readPacket(*theFragmentStream->theStream, &theFragmentStream->theBuffer[0] + theFragmentStream->bytes_in_buffer,
+                                         theFragmentStream->theBuffer.size() - 1 - theFragmentStream->bytes_in_buffer);
   if (numChars < 0)
   {
     theXQueryDiagnostics->add_error(NEW_ZORBA_EXCEPTION(zerr::ZSTR0020_LOADER_IO_ERROR));
@@ -171,13 +171,12 @@
 
   theFragmentStream->bytes_in_buffer += numChars;
   theFragmentStream->current_offset = 0;
-  theFragmentStream->ctxt->input->base = (xmlChar*)(theFragmentStream->theBuffer);
-  theFragmentStream->ctxt->input->length = (theFragmentStream->bytes_in_buffer < FragmentIStream::BUFFER_SIZE? theFragmentStream->bytes_in_buffer : FragmentIStream::BUFFER_SIZE);
+  theFragmentStream->ctxt->input->base = (xmlChar*)(&theFragmentStream->theBuffer[0]);
+  theFragmentStream->ctxt->input->length = (theFragmentStream->bytes_in_buffer < (theFragmentStream->theBuffer.size()-1) ? theFragmentStream->bytes_in_buffer : (theFragmentStream->theBuffer.size()-1));
   theFragmentStream->ctxt->input->cur = theFragmentStream->ctxt->input->base;
   theFragmentStream->ctxt->input->end = theFragmentStream->ctxt->input->base + theFragmentStream->ctxt->input->length;
-  theFragmentStream->ctxt->checkIndex = 0;
-
-  if (theFragmentStream->bytes_in_buffer < FragmentIStream::BUFFER_SIZE+FragmentIStream::LOOKAHEAD_BYTES)
+  
+  if (theFragmentStream->bytes_in_buffer < theFragmentStream->theBuffer.size()-1)
     theFragmentStream->theBuffer[theFragmentStream->bytes_in_buffer] = 0;
 
   return !theFragmentStream->stream_is_consumed();
@@ -212,12 +211,12 @@
     theFragmentStream = static_cast<FragmentIStream*>(&stream);
 
     // Prepare the input buffer and the parser context
-    if (theFragmentStream->theBuffer == NULL)
+    if (theFragmentStream->theBuffer.size() == 0)
     {
       // Allocate input buffer
-      theFragmentStream->theBuffer = new char[FragmentIStream::BUFFER_SIZE + FragmentIStream::LOOKAHEAD_BYTES+1];
-      theFragmentStream->theBuffer[FragmentIStream::BUFFER_SIZE + FragmentIStream::LOOKAHEAD_BYTES] = 0;
-
+      theFragmentStream->theBuffer.resize(FragmentIStream::DEFAULT_BUFFER_SIZE + 1);
+      theFragmentStream->theBuffer[FragmentIStream::DEFAULT_BUFFER_SIZE] = 0;
+      
       // Create the LibXml parser context
       theFragmentStream->ctxt = xmlCreatePushParserCtxt(&theSaxHandler, this, NULL, 0, 0);
       if (theFragmentStream->ctxt == NULL)
@@ -242,7 +241,7 @@
 
       // Initialize the parser input (only filename and the pointer to the current char)
       theFragmentStream->theBuffer[0] = ' '; // This assignment is needed for LibXml2-2.7.6, which tries to read the buffer when xmlPushInput() is called
-      input->cur = (xmlChar*)(theFragmentStream->theBuffer);
+      input->cur = (xmlChar*)(&theFragmentStream->theBuffer[0]);
       input->filename = (const char*)(xmlCanonicPath((const xmlChar*)theDocUri.c_str()));
       xmlPushInput(theFragmentStream->ctxt, input);
     }
@@ -316,7 +315,7 @@
         }
       }
       
-      /*      
+      /*
       std::cerr << "\n==================\n--> skip_root: " << theFragmentStream->root_elements_to_skip << " current_depth: " << theFragmentStream->current_element_depth 
           << " state: " << theFragmentStream->ctxt->instate 
           << " about to parse: [" << theFragmentStream->ctxt->input->cur << "] " << std::endl;
@@ -326,14 +325,21 @@
                     theFragmentStream->ctxt->input->length, 0);
 
       // If we didn't get an error and we haven't moved, we might have some freestanding text. Parse it as element character data.
-      if (theXQueryDiagnostics->errors().empty()
-          &&
-          theFragmentStream->current_offset == 0)
+      if (theXQueryDiagnostics->errors().empty() && theFragmentStream->current_offset == 0)
       {
         if (theFragmentStream->state == FragmentIStream::FRAGMENT_FIRST_START_DOC)
           FragmentXmlLoader::startDocument(theFragmentStream->ctxt->userData);
         xmlParseCharData(theFragmentStream->ctxt, 0);
         theFragmentStream->current_offset = getCurrentInputOffset(); // update current offset
+        
+        if (theXQueryDiagnostics->errors().empty() && theFragmentStream->current_offset == 0 && theFragmentStream->ctxt->checkIndex > 0)
+        {
+          // we still haven't moved, double the buffer size
+          theFragmentStream->theBuffer.resize((theFragmentStream->theBuffer.size()-1) * 2 + 1);
+          theFragmentStream->ctxt->input->base = (xmlChar*)(&theFragmentStream->theBuffer[0]);
+          theFragmentStream->ctxt->input->cur = theFragmentStream->ctxt->input->base;
+          theFragmentStream->ctxt->input->end = theFragmentStream->ctxt->input->base + theFragmentStream->ctxt->input->length;
+        }
       }
 
       if ( ! theXQueryDiagnostics->errors().empty())

=== modified file 'src/store/naive/loader_fast.cpp'
--- src/store/naive/loader_fast.cpp	2012-07-24 08:48:48 +0000
+++ src/store/naive/loader_fast.cpp	2012-08-01 22:40:26 +0000
@@ -668,16 +668,6 @@
                   << std::endl << " ordpath = " << elemNode->getOrdPath().show()
                   << std::endl);
     
-    // Add the base-uri if the parent document node is not being created, which happens when xml fragments are parsed
-    FragmentXmlLoader* fragmentLoader = dynamic_cast<FragmentXmlLoader*>(&loader);
-    if (fragmentLoader != NULL && 
-        fragmentLoader->theLoadProperties.getCreateDocParentLink() == false &&
-        fragmentLoader->getFragmentStream()->current_element_depth == 1)
-    {
-      zstring emptyStr;
-      elemNode->addBaseUriProperty(loader.theBaseUri, emptyStr);
-    }
-
     // Process namespace bindings
     if (numBindings > 0)
     {
@@ -767,6 +757,16 @@
                       << attrNode->getOrdPath().show() << std::endl);
       }
     }
+    
+    // Add the base-uri if the parent document node is not being created, which happens when xml fragments are parsed
+    FragmentXmlLoader* fragmentLoader = dynamic_cast<FragmentXmlLoader*>(&loader);
+    if (fragmentLoader != NULL && 
+        fragmentLoader->theLoadProperties.getCreateDocParentLink() == false &&
+        fragmentLoader->getFragmentStream()->current_element_depth == 1)
+    {
+      zstring emptyStr;
+      elemNode->addBaseUriProperty(loader.theBaseUri, emptyStr);
+    }
 
     nodeStack.push((XmlNode*)elemNode);
     nodeStack.push(NULL);

=== added file 'test/rbkt/ExpQueryResults/zorba/parsing_and_serializing/parse-xml-fragment-31.xml.res'
--- test/rbkt/ExpQueryResults/zorba/parsing_and_serializing/parse-xml-fragment-31.xml.res	1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/parsing_and_serializing/parse-xml-fragment-31.xml.res	2012-08-01 22:40:26 +0000
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ret><page/></ret><ret>
+</ret>
\ No newline at end of file

=== added file 'test/rbkt/ExpQueryResults/zorba/parsing_and_serializing/parse-xml-fragment-32.xml.res'
--- test/rbkt/ExpQueryResults/zorba/parsing_and_serializing/parse-xml-fragment-32.xml.res	1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/parsing_and_serializing/parse-xml-fragment-32.xml.res	2012-08-01 22:40:26 +0000
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template head="test"/>
\ No newline at end of file

=== added file 'test/rbkt/ExpQueryResults/zorba/parsing_and_serializing/parse-xml-fragment-33.xml.res'
--- test/rbkt/ExpQueryResults/zorba/parsing_and_serializing/parse-xml-fragment-33.xml.res	1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/parsing_and_serializing/parse-xml-fragment-33.xml.res	2012-08-01 22:40:26 +0000
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<title xmlns:xml_split="http://xmltwig.com/xml_split";>Accounting</title>
\ No newline at end of file

=== added file 'test/rbkt/Queries/zorba/parsing_and_serializing/parse-xml-fragment-31.xq'
--- test/rbkt/Queries/zorba/parsing_and_serializing/parse-xml-fragment-31.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/parsing_and_serializing/parse-xml-fragment-31.xq	2012-08-01 22:40:26 +0000
@@ -0,0 +1,16 @@
+(:
+  Test parse-xml:parse() with streamable input
+:)
+
+import module namespace fetch = "http://www.zorba-xquery.com/modules/fetch";;
+import module namespace parse-xml = "http://www.zorba-xquery.com/modules/xml";;
+import schema namespace opt = "http://www.zorba-xquery.com/modules/xml-options";;
+
+let $xmlcontents := fetch:content(fn:resolve-uri("streamable.xml"))
+let $contents := parse-xml:parse(
+      $xmlcontents,
+      <opt:options>
+          <opt:parse-external-parsed-entity opt:skip-root-nodes="0" />
+      </opt:options>)
+for $article at $pos in $contents
+return <ret>{ $article }</ret>

=== added file 'test/rbkt/Queries/zorba/parsing_and_serializing/parse-xml-fragment-32.xq'
--- test/rbkt/Queries/zorba/parsing_and_serializing/parse-xml-fragment-32.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/parsing_and_serializing/parse-xml-fragment-32.xq	2012-08-01 22:40:26 +0000
@@ -0,0 +1,12 @@
+(:
+  Test parse-xml:parse() with a single top element which has an attribute
+:)
+
+import module namespace parse-xml = "http://www.zorba-xquery.com/modules/xml";;
+import schema namespace opt = "http://www.zorba-xquery.com/modules/xml-options";;
+
+parse-xml:parse(
+  "<template head='test'></template>",
+  <opt:options>
+    <opt:parse-external-parsed-entity opt:skip-root-nodes="0"/>
+  </opt:options>)

=== added file 'test/rbkt/Queries/zorba/parsing_and_serializing/parse-xml-fragment-33.xq'
--- test/rbkt/Queries/zorba/parsing_and_serializing/parse-xml-fragment-33.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/parsing_and_serializing/parse-xml-fragment-33.xq	2012-08-01 22:40:26 +0000
@@ -0,0 +1,16 @@
+import module namespace parse-xml = "http://www.zorba-xquery.com/modules/xml";;
+import schema namespace opt = "http://www.zorba-xquery.com/modules/xml-options";;
+import module namespace fetch = "http://www.zorba-xquery.com/modules/fetch";;
+
+variable $xmlcontents := fetch:content(resolve-uri("wiki.xml"));
+
+let $contents := parse-xml:parse(
+      $xmlcontents,
+      <opt:options>
+        <opt:parse-external-parsed-entity opt:skip-root-nodes="1" />
+      </opt:options>)
+
+for $article at $pos in $contents
+let $title := $article//title
+return
+    $title

=== added file 'test/rbkt/Queries/zorba/parsing_and_serializing/streamable.xml'
--- test/rbkt/Queries/zorba/parsing_and_serializing/streamable.xml	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/parsing_and_serializing/streamable.xml	2012-08-01 22:40:26 +0000
@@ -0,0 +1,1 @@
+<page />

=== added file 'test/rbkt/Queries/zorba/parsing_and_serializing/wiki.xml'
--- test/rbkt/Queries/zorba/parsing_and_serializing/wiki.xml	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/parsing_and_serializing/wiki.xml	2012-08-01 22:40:26 +0000
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<xml_split:root xmlns:xml_split="http://xmltwig.com/xml_split";>
+  <page>
+    <title>Accounting</title>
+    <revision>
+      <text xml:space="preserve"><redirect><link label="Accountancy">Accountancy</link><template head="R from related word}
+Aѕ lоng аѕ thеrе wіll реорlе іn thіѕ wоrld, thеrе wіll bе buѕіnеѕѕ, аnd аѕ lоng аѕ thеrе іѕ buѕіnеѕѕ, thеrе wіll bе ассоuntіng. Aссоuntіng іѕ іnvоlvеd іn vіrtuаllу еvеrуthіng іn оur lіvеѕ whеthеr wе knоw іt оr nоt, аnd іtѕ іmроrtаnсе саn ѕоmеtіmеѕ bе оvеrlооkеd. Evеrуthіng nееdѕ ассоuntіng. Frоm а bаѕеbаll сар уоu wеаr, tо thе fооd уоu еаt, tо thе соmраnу уоu wоrk fоr, оr еvеn thе muѕіс уоu hеаr оn thе rаdіо, ассоuntіng іѕ nееdеd аnd wаѕ іnvоlvеd іn аll оf thоѕе thіngѕ ѕtерѕ оf рrоduсtіоn оr ореrаtіоn.
+
+An ассоuntаnt mеаѕurеѕ, аggrеgаtеѕ аnd rероrtѕ fіnаnсіаl іnfоrmаtіоn nесеѕѕаrу fоr thе ѕаkе оf dесіѕіоn mаkіng bу оrgаnіzаtіоnаl mаnаgеrѕ, оwnеrѕ, іnvеѕtоrѕ, gоvеrnmеnt аgеnсіеѕ аnd оthеr uѕеrѕ. Frоm thе рrеvіоuѕ ѕtаtеmеnt, wе саn ѕее thе іndісаtіоn thаt ѕhоwѕ hоw ассоuntіng іѕ іmроrtаnt іn саrееrѕ. Evеrуthіng nееdѕ ассоuntіng. Whеthеr уоu аrе а dосtоr, оr аn оrdіnаrу оffісе wоrkеr, уоu wоuld ѕtіll nееd tо рlау wіth numbеrѕ аnd tаkе іntо ассоunt thе соѕt оf thіngѕ thаt уоu uѕеd оr uѕеd bу ѕоmеоnе еlѕе. Tіmеlу аnd ассurаtе іnfоrmаtіоn оn ореrаtіоnѕ іѕ сruсіаl tо ѕuссеѕѕ іn thе rесеnt dауѕ оf rаріdlу сhаngіng buѕіnеѕѕ еnvіrоnmеnt, аnd thе ассоuntаnt іѕ thе оnе rеѕроnѕіblе fоr thе сrіtісаl buѕіnеѕѕ funсtіоn thаt mоѕtlу thоugh іndіrесtlу аffесtѕ оur саrееr аnd еvеrуdау lіfе.
+
+In thе саrееr реrѕресtіvе еvеrу оссuраtіоn muѕt саrеfullу mаnаgе thеіr mоnеу, thеіr саѕh іnflоwѕ аnd оutflоwѕ. Jоbѕ ѕuсh аѕ іndереndеnt соntrасtоrѕ hаvе tо аnаlуzе hоw muсh thе јоb іѕ gоіng tо соѕt thеm аnd hоw muсh thеу аrе gоіng tо сhаrgе fоr thе јоb. Thеу muѕt trасk аll thеіr еxреnѕеѕ аnd еѕtіmаtеѕ іn оrdеr tо nоt undеr соѕt аnd tо сhаrgе аррrорrіаtе рrісеѕ. In ѕіmрlеѕt fоrm, thіѕ іѕ ассоuntіng. If nоt ассurаtеlу trасkеd аnd еѕtіmаtеd, thаt соntrасtоr'ѕ buѕіnеѕѕ wіll nоt рrоfіt аnd еvеntuаllу gо оut оf buѕіnеѕѕ. Sаmе thіng gоеѕ fоr bіg buѕіnеѕѕ, іf thаt соmраnу оr fіrmѕ dоеѕ nоt ассurаtеlу hаndlе thе ореrаtіоnѕ оf іtѕ саѕh flоwѕ, іt wіll nоt ѕuѕtаіn fоr lоng. Mоrе аnd mоrе tоdау еmрlоуееѕ ѕuсh аѕ ѕесrеtаrіеѕ аnd rесерtіоnіѕtѕ аrе bеіng tаught bаѕіс ассоuntіng рrасtісеѕ tо hеlр аіd thе ассоuntаntѕ аnd tо kеер рrореr bооkkееріng. Thіѕ wау thеѕе еmрlоуееѕ саn mаnаgе thе lоwеr іmроrtаnсе іtеmѕ оf а buѕіnеѕѕ аnd lеаvе trаnѕасtіоnѕ оf hіghеr ѕіgnіfісаnсе tо thе ассоuntаnt tо аnаlуzе аnd іntеrрrеt fоr dесіѕіоn mаkіng рurроѕеѕ.
+[http://whatacountingis.com What Accounting Is All About]
+
+Hаndlіng аll thе саlсulаtіоnѕ оf ассоuntіng саn bе tеdіоuѕ аnd bоthеrѕоmе but саn mаkе аll thе dіffеrеnсе оnе'ѕ fіnаnсеѕ. Yоu саn ѕtrаtеgісаllу рlаn аhеаd tо ѕаvе fоr уоur rеtіrеmеnt, уоur сhіldrеn'ѕ соllеgе fund, оr luxurу оr twо lаtеr dоwn thе rоаd. Wе саn ѕее thе іmроrtаnсе оf thіѕ соnсерt bу nоtісіng соmраnіеѕ tоdау whоѕе ореrаtіоnѕ аrе ѕtrісtlу fіnаnсіаl аdvіѕіng. It саn bе соnсludеd thаt іn thе еrа thаt wе аrе lіvіng іn tоdау, Fаmіlіеѕ, аnd rеtіrееѕ dереnd оn thе сrеdіbіlіtу оf fіnаnсіаl rероrtіng fоr thеіr futurеѕ аnd lіvеlіhооdѕ. Wіth fіnаnсіаl рrеѕѕurе аmоuntіng іn mаnу реорlеѕ' lіvеѕ, ассоuntіng іѕ ѕееn аѕ іmроrtаnt іn ѕо mаnу wауѕ thаt іt іѕ аlmоѕt іmроѕѕіblе tо lіvе wіthоut іt.
+[http://whatacountingis.com What Accounting Is All About]"></template></redirect></text>
+    </revision>
+  </page>
+</xml_split:root>

-- 
Mailing list: https://launchpad.net/~zorba-coders
Post to     : zorba-coders@lists.launchpad.net
Unsubscribe : https://launchpad.net/~zorba-coders
More help   : https://help.launchpad.net/ListHelp

Reply via email to