Back Midas Rome Roody Rootana
  Rome Analyzer Framework, Page 5 of 11  Not logged in ELOG logo
IDup Date Author Topic Subject
  81   06 May 2005 Ryu SawadaInfoIdentical check in builder
About identical check of new and existing file in builder.

Currently there is a limit of file size defined by "bufferLength".
When the file size exceed the size, the file will be always overwritten by builder.
And if the file is existing task cpp file, user will lose a part of his code.

Actually, LPConfig.cpp is larger than that.

So I wrote a simple function. it will be called recursively when the file is larger than buffer.
bufferLength is not necessary to be so large.

bool checkIdentical(const int fileHandle,const char* pattern,bool flag=true)
{
   static int position
   int  i;
   char fileBuffer[bufferLength];

   if(flag)
      position = 0;

   int nb = read(fileHandle,&fileBuffer, sizeof(fileBuffer));
  
   if(nb == bufferLength){
      if((int)strlen(pattern)-position < bufferLength)
         return false;
     
      for (i=0;i<nb;i++)
         if (pattern[position+i] != fileBuffer[i])
            return false;     
      position += bufferLength;
      return checkIdentical(fileHandle,pattern,false);
   }  
   else{
      if (nb != (int)strlen(pattern) - position)
         return false;
     
      for (i=0;i<nb;i++)
         if (pattern[position+i] != fileBuffer[i])
            return false;
   }
   return true;
}


You may use this function in builder like,
   int fileHandle = open("test.txt",O_RDONLY);
   bool identical = checkIdentical(fileHandle,buffer);
   close(fileHandle);

And there is another way. TString can read file. this featrue can be used when you read existing Task cpp file.

   ifstream ifile("test.txt");
   TString fileBuffer;
   str.ReadFile(ifile);
   bool identical = ( fileBuffer == buffer );
  82   07 May 2005 Ryu SawadaBug ReportMismatch of [Set/Append]Formatted
In ROMEBuilder.cpp, there are some mismatchs of formart and argument of ROMEString::Set or 
AppendFormatted.

1000       format.SetFormatted("   void Set%%s%%%ds(%%-%ds %%s%%%ds) { f%%s%%%ds = %%s; };
\n",lb,typeLen,lb,lb,lb); 

3980             format.SetFormatted("   %%s%%s*%%%ds  Get%%sAt(int index)%%%ds\n",typeLen-
folderName[i].Length()-scl,0+nameLen-folderName[i].Length(),lt);

5875    buffer.AppendFormatted("   bool CheckConfiguration(int runNumber);\n",shortCut.Data()); 

6487    buffer.AppendFormatted("#include <TBranchElement.h>\n",shortCut.Data()); 

7231       buffer.AppendFormatted("   return buffer;\n",stringBuffer.Data());

7271                buffer.AppendFormatted("   if (!this->GetDataBase(name.Data())->Read
(values,path,gAnalyzer->GetCurrentRunNumber())) {\n",folderName[i].Data(),valueName[i][j].Data());

7358          buffer.AppendFormatted("%s                  xml->WriteElement(\"HistArrayStartIndex\",\"0\");
\n",blank.Data(),pointerI.Data(),histoName[taskHierarchyClassIndex[i]][j].Data()); 

8771    buffer.AppendFormatted(" obj/%sAnalyzer.obj obj/%sEventLoop.obj obj/%sConfig.obj obj/
main.obj",shortCut.Data(),shortCut.Data(),shortCut.Data(),shortCut.Data(),shortCut.Data());

8815    buffer.AppendFormatted("     g++ $(Flags) -o $@ $(objects) $(Libraries)\n\n",shortCut.Data
(),mainProgName.Data());

8872    buffer.AppendFormatted("obj/%sFAnalyzer.obj: src/framework/%sFAnalyzer.f src/framework/%
sAnalyzer.cpp include/framework/%sAnalyzer.h\n",shortCut.Data(),shortCut.Data(),shortCut.Data());

9341    htmlFile.SetFormatted("%s%sUserHTML.html",outDir.Data(),shortCut.Data(),mainProgName.Data
());
  83   13 May 2005 Ryu SawadaInfoIdentical check in builder
done.

> About identical check of new and existing file in builder.
> 
> Currently there is a limit of file size defined by "bufferLength".
> When the file size exceed the size, the file will be always overwritten by builder.
> And if the file is existing task cpp file, user will lose a part of his code.
> 
> Actually, LPConfig.cpp is larger than that.
> 
> So I wrote a simple function. it will be called recursively when the file is larger than buffer.
> bufferLength is not necessary to be so large.
> 
> bool checkIdentical(const int fileHandle,const char* pattern,bool flag=true)
> {
>    static int position
>    int  i;
>    char fileBuffer[bufferLength];
> 
>    if(flag)
>       position = 0;
> 
>    int nb = read(fileHandle,&fileBuffer, sizeof(fileBuffer));
>   
>    if(nb == bufferLength){
>       if((int)strlen(pattern)-position < bufferLength)
>          return false;
>      
>       for (i=0;i<nb;i++)
>          if (pattern[position+i] != fileBuffer[i])
>             return false;     
>       position += bufferLength;
>       return checkIdentical(fileHandle,pattern,false);
>    }  
>    else{
>       if (nb != (int)strlen(pattern) - position)
>          return false;
>      
>       for (i=0;i<nb;i++)
>          if (pattern[position+i] != fileBuffer[i])
>             return false;
>    }
>    return true;
> }
> 
> 
> You may use this function in builder like,
>    int fileHandle = open("test.txt",O_RDONLY);
>    bool identical = checkIdentical(fileHandle,buffer);
>    close(fileHandle);
> 
> And there is another way. TString can read file. this featrue can be used when you read existing Task cpp file.
> 
>    ifstream ifile("test.txt");
>    TString fileBuffer;
>    str.ReadFile(ifile);
>    bool identical = ( fileBuffer == buffer );
  84   13 May 2005 Ryu SawadaBug ReportMismatch of [Set/Append]Formatted
done.
> In ROMEBuilder.cpp, there are some mismatchs of formart and argument of ROMEString::Set or 
> AppendFormatted.
> 
> 1000       format.SetFormatted("   void Set%%s%%%ds(%%-%ds %%s%%%ds) { f%%s%%%ds = %%s; };
> \n",lb,typeLen,lb,lb,lb); 
> 
> 3980             format.SetFormatted("   %%s%%s*%%%ds  Get%%sAt(int index)%%%ds\n",typeLen-
> folderName[i].Length()-scl,0+nameLen-folderName[i].Length(),lt);
> 
> 5875    buffer.AppendFormatted("   bool CheckConfiguration(int runNumber);\n",shortCut.Data()); 
> 
> 6487    buffer.AppendFormatted("#include <TBranchElement.h>\n",shortCut.Data()); 
> 
> 7231       buffer.AppendFormatted("   return buffer;\n",stringBuffer.Data());
> 
> 7271                buffer.AppendFormatted("   if (!this->GetDataBase(name.Data())->Read
> (values,path,gAnalyzer->GetCurrentRunNumber())) {\n",folderName[i].Data(),valueName[i][j].Data());
> 
> 7358          buffer.AppendFormatted("%s                  xml->WriteElement(\"HistArrayStartIndex\",\"0\");
> \n",blank.Data(),pointerI.Data(),histoName[taskHierarchyClassIndex[i]][j].Data()); 
> 
> 8771    buffer.AppendFormatted(" obj/%sAnalyzer.obj obj/%sEventLoop.obj obj/%sConfig.obj obj/
> main.obj",shortCut.Data(),shortCut.Data(),shortCut.Data(),shortCut.Data(),shortCut.Data());
> 
> 8815    buffer.AppendFormatted("     g++ $(Flags) -o $@ $(objects) $(Libraries)\n\n",shortCut.Data
> (),mainProgName.Data());
> 
> 8872    buffer.AppendFormatted("obj/%sFAnalyzer.obj: src/framework/%sFAnalyzer.f src/framework/%
> sAnalyzer.cpp include/framework/%sAnalyzer.h\n",shortCut.Data(),shortCut.Data(),shortCut.Data());
> 
> 9341    htmlFile.SetFormatted("%s%sUserHTML.html",outDir.Data(),shortCut.Data(),mainProgName.Data
> ());
  85   01 Jun 2005 Ryu SawadaForumTaskHierarchy
This is a proposal on specifying relation of tasks.

Currently, ROME has task hierarchy system. It may be enough.
But a missing thing is that we can not use "||".

So a possibility is like this.

For example, there are two calibration tasks B and D, and a calculation task A requires one of B or D.
B and D dependes on data filling task C.

This case, one writes down the relation in definition file.
<TaskDependenses>
  <TaskDependense taskname="A"/> B || D </TaskDependense>
  <TaskDependense taskname="B"/> C </TaskDependense>
  <TaskDependense taskname="D"/> C && !B </TaskDependense>
</TaskDependeses>

Then, builder analyzes the relation and re-arranges the execution order like,
C->B->D->A

At run time, ROMEEventLoop checks if depending tasks were executed before calling a task. And when 
B or C is executed, it calls A.

This way users can specify complicated relation like ((A && B) || C ) || !D )....

A problem is that you can not express the relation with indent in configuration file and documentation.


This is just a proposal, I'm not sure which is better, TaskHierarcy or TaskDependense.
Personaly, I am not using task controll.
  86   11 Jun 2005 Ryu SawadaSuggestionclass members in TTree
I think that fUniqueID and fBits in TObject is not useful to store in TTree.
I found in ROOT users guide that we can disable that with
MyClass::Class()->IgnoreTObjectStreamer(); 
.

If you also think these are useless information, please implement it in ROME.



second topic.
This is just a matter of favor.
We sometimes use TTree interactively. At that time, I feel fRunNumber, fEventNumber, fTimeStamp, 
fSequentialNumber are long to type.
I prefer shorter without upper case letter.
If many people agree it, I would like to ask to change them run,event,timestamp,number like MIDAS analyzer.
  87   26 Jun 2005 Yasuko HISAMATSUBug ReportNeed to include stdarg.h in file ROMEString.h
Hello,

It seems some compliers have a problem with making ROME, sending error message
saying,

 In file included from builder/src/ROMEBuilder.cpp:574:
/home/pekochan/rome/20050626/rome/include/ROMEString.h:41: type spec ifier
   omitted for parameter `va_list'
 ...etc


This problem can be fixed in file "ROMEString.h" by adding the following 
either line;

#include <stdarg.h>

or 

#include <Varargs.h>
  88   27 Jun 2005 Matthias SchneebeliBug ReportNeed to include stdarg.h in file ROMEString.h
> Hello,
> 
> It seems some compliers have a problem with making ROME, sending error message
> saying,
> 
>  In file included from builder/src/ROMEBuilder.cpp:574:
> /home/pekochan/rome/20050626/rome/include/ROMEString.h:41: type spec ifier
>    omitted for parameter `va_list'
>  ...etc
> 
> 
> This problem can be fixed in file "ROMEString.h" by adding the following 
> either line;
> 
> #include <stdarg.h>
> 
> or 
> 
> #include <Varargs.h>


Thanks for reporting that problem. 

#include <stdarg.h>
is added now in the cvs.
  89   28 Jun 2005 Ryu SawadaSuggestionFolder reset.
In Reset method of folders. Support folder members are ignored currently.
Reset method of support folder member had better to be called.

In addition, isModified() should check if the member support folders were modified.

For example

class A : public TObject
{
protected:
   TypeB* B;
   TClonesArray* C;
.
.
public:
   A() {
      B = 0;
      C = 0;
   };
.
.
.
   void Reset() {
      int i;
      fModified = false;
      if(B) B->Reset();
      if(C) for(i=0;i<C->GetEntries();i++) { ((TypeC*)C->At(i))->Reset(); }
   };

   ClassDef(A,1)
};
  90   28 Jun 2005 Matthias SchneebeliSuggestionclass members in TTree
> I think that fUniqueID and fBits in TObject is not useful to store in TTree.
> I found in ROOT users guide that we can disable that with
> MyClass::Class()->IgnoreTObjectStreamer(); 
> .
> 
> If you also think these are useless information, please implement it in ROME.
> 
> 
> 
> second topic.
> This is just a matter of favor.
> We sometimes use TTree interactively. At that time, I feel fRunNumber, fEventNumber, fTimeStamp, 
> fSequentialNumber are long to type.
> I prefer shorter without upper case letter.
> If many people agree it, I would like to ask to change them run,event,timestamp,number like MIDAS analyzer.

MyClass::Class()->IgnoreTObjectStreamer(); is implemented.
  91   28 Jun 2005 Ryu SawadaBug ReportNeed to include stdarg.h in file ROMEString.h
> > Hello,
> > 
> > It seems some compliers have a problem with making ROME, sending error message
> > saying,
> > 
> >  In file included from builder/src/ROMEBuilder.cpp:574:
> > /home/pekochan/rome/20050626/rome/include/ROMEString.h:41: type spec ifier
> >    omitted for parameter `va_list'
> >  ...etc
> > 
> > 
> > This problem can be fixed in file "ROMEString.h" by adding the following 
> > either line;
> > 
> > #include <stdarg.h>
> > 
> > or 
> > 
> > #include <Varargs.h>
> 
> 
> Thanks for reporting that problem. 
> 
> #include <stdarg.h>
> is added now in the cvs.
replaced with #include <Varargs.h>
  92   28 Jun 2005 Ryu SawadaSuggestionclass members in TTree
> This is just a matter of favor.
> We sometimes use TTree interactively. At that time, I feel fRunNumber, fEventNumber, fTimeStamp, 
> fSequentialNumber are long to type.
> I prefer shorter without upper case letter.
> If many people agree it, I would like to ask to change them run,event,timestamp,number like MIDAS analyzer.
changed to run, event, time and number.
  93   28 Jun 2005 Ryu SawadaBug Reportsupport folder object in folder.
When one makes a support folder type field in folder, pointer to the support folder or pointer of 
TClonesArray is in the folder.
But There is no "new" statement to create the object.
  94   10 Jul 2005 Ryu SawadaSuggestionResetFolders
ResetFolders are called in DAQEvent for every events.

I have some folders who's information have to be kept during the run.

One possibility is to add new tag like <ResetBoforeEvents> in folder definition.
The other is give up reset in ROMEEventLoop and give users resposibility of reset.
  95   21 Jul 2005 Ryu SawadaSuggestionResetFolders
> ResetFolders are called in DAQEvent for every events.
> 
> I have some folders who's information have to be kept during the run.
> 
> One possibility is to add new tag like <ResetBoforeEvents> in folder definition.
> The other is give up reset in ROMEEventLoop and give users resposibility of reset.

done.

The name of the tag is <NoResetByFramework>
  96   21 Jul 2005 Ryu SawadaSuggestionAn idea of file I/O
Current file handling of ROME is based on run number. This way is simple and easy to understand. But 
there are some arguments on it.

(i) In case of .mid file. ROME assume the file name is like run00001.mid. But this is not always true. 
MIDAS users can have different name.

(ii) ROME saves one TTree in one file. But there are cases that user want to save several TTrees in a file

(iii) Especially rare event search experiments like MEG, one file may contain signal candidate events 
from several runs.

To solve this issue one idea is to move from run base I/O to filename base I/O.

* Input
Users may specify filename instead of run number with command line option like.
XXXanalyzer.exe -f run00001.mid,run00002.mid

Then ROMEAnalyzer provides information of filename to DAQ class.

How files are handled is depending on DAQ class. Normally each files will be read one by one.
But in case of bartender program, they will be read in parallel to mix.

In case of .mid file, run number is written in run header of files.
ROMEMidas should change current run number at the BeginOfRun.

* Output
For the output filename we can employ same technique as database path.
This way users can use also run number and some other parameters.
And they can store several TTrees in a file.

  <Tree>
      <TreeName>tree</TreeName>
      <TreeOutputFileName>"%s#.root",gAnalyzer->GetGSP()->GetFilePrefix()<TreeOutputFileName>
      <Branch>
        <BranchName>kine</BranchName>
        <RelatedFolder>Kinematics</RelatedFolder>
      </Branch>
  </Tree>
 

This change is quite big, and it affects all ROME users.
We have to discuss enough on it before changing.

(One choice is to remain run number base I/O.)
  97   29 Aug 2005 Ryu SawadaBug ReportTFile creation and TTree
When the filename base IO was added, it seems that TFile creation was moved from 
ROMEEventLoop::DAQInit to ROMEEventLoop:DAQEndOfRun.

So ROME makes TTrees without outputfile.

This is dangerous when a TTree becomes huge.
It can consume large amount of memory.
  98   30 Aug 2005 Matthias SchneebeliBug ReportTFile creation and TTree
> When the filename base IO was added, it seems that TFile creation was moved from 
> ROMEEventLoop::DAQInit to ROMEEventLoop:DAQEndOfRun.
> 
> So ROME makes TTrees without outputfile.
> 
> This is dangerous when a TTree becomes huge.
> It can consume large amount of memory.

Your right. I have changed it back.
  99   08 Sep 2005 Ryu SawadaForumDividing xml the definition xml file.
I am thinking to divide my definition xml file into several files.

I do not know if there is a general way, but I found some web pages mentioning about it. According to 
these pages we can include an xml document to  other one like.

---------- booklist.xml ------------
<?xml version="1.0"?>
<!DOCTYPE books [
<!ENTITY book1 SYSTEM "book1.xml">
]>

<books>
  &book1;
</books>
------------------------------------
------------ book1.xml -------------
<?xml version="1.0"?>
<book>
  <title>Title of the book</title>
  <author>Author of the book</author>
</book>
------------------------------------

Is it possible to do it with mxml ?


I have two reasons.
1.
 I have two ROME programs.  I call them as "writer" and "reader" in this message. "reader" reads output 
file from "writer". In this case, "reader" needs to know the structure of branch folder defined in "writer"'s 
xml file.
What I am doing is adding headers of "writer" to DictionaryHeaders in Makefile.user of "reader". At least, 
it works, but this way I have to write reading function of trees by hand. On the other hand, If it is 
possible to include a part of xml file of "writer" in that of "reader", romebuilder can create proper 
functions automatically.

2.
 My project definition file is already big. It has more than 2000 lines. If one has good XML editor, it may 
not be a problem. But unfortunately I do not know good editor on Linux. What I am thinking is making a 
directory structure like this.
.
|-- main_definition.xml
`-- xml
    |-- folders
    |   |-- folder_1.xml
    |   |-- folder_2.xml
    |   `-- folder_3.xml
    `-- tasks
        |-- task_1.xml
        |-- task_2.xml
        `-- task_3.xml

folder_[1-3].xml and task_[1-3].xml will be included in main_definition.xml.
  100   06 Oct 2005 Ryu SawadaForumDividing xml the definition xml file.
> I am thinking to divide my definition xml file into several files.
> 
> I do not know if there is a general way, but I found some web pages mentioning about it. According to 
> these pages we can include an xml document to  other one like.
> 
> ---------- booklist.xml ------------
> <?xml version="1.0"?>
> <!DOCTYPE books [
> <!ENTITY book1 SYSTEM "book1.xml">
> ]>
> 
> <books>
>   &book1;
> </books>
> ------------------------------------
> ------------ book1.xml -------------
> <?xml version="1.0"?>
> <book>
>   <title>Title of the book</title>
>   <author>Author of the book</author>
> </book>
> ------------------------------------
> 
> Is it possible to do it with mxml ?

I made a patch to enable it with mxml.
Please test it.

If it is reliable, I will commit it.

----
cd $ROMESYS
patch -p0 < mxml.diff  
----
Attachment 1: mxml.diff
Index: include/mxml.h
===================================================================
RCS file: /usr/local/cvsroot/rome/include/mxml.h,v
retrieving revision 1.7
diff -u -r1.7 mxml.h
--- include/mxml.h	12 Jul 2005 09:04:15 -0000	1.7
+++ include/mxml.h	6 Oct 2005 21:53:13 -0000
@@ -43,6 +43,10 @@
 #define PROCESSING_INSTRUCTION_NODE   3
 #define COMMENT_NODE                  4
 #define DOCUMENT_NODE                 5
+#define ENTITY_NODE                   6
+
+#define INTERNAL_ENTITY               0
+#define EXTERNAL_ENTITY               1
 
 typedef struct {
    int  fh;
@@ -115,6 +119,7 @@
 PMXML_NODE mxml_create_root_node();
 PMXML_NODE mxml_parse_file(char *file_name, char *error, int error_size);
 PMXML_NODE mxml_parse_buffer(char *buffer, char *error, int error_size);
+PMXML_NODE mxml_parse_entity(char *buf, char *error, int error_size);
 int mxml_write_tree(char *file_name, PMXML_NODE tree);
 void mxml_debug_tree(PMXML_NODE tree, int level);
 void mxml_free_tree(PMXML_NODE tree);
Index: src/mxml.c
===================================================================
RCS file: /usr/local/cvsroot/rome/src/mxml.c,v
retrieving revision 1.8
diff -u -r1.8 mxml.c
--- src/mxml.c	11 May 2005 12:50:02 -0000	1.8
+++ src/mxml.c	6 Oct 2005 21:53:13 -0000
@@ -1209,6 +1209,30 @@
 
             p += 2;
 
+         } else if (strncmp(p, "!ENTITY", 7) == 0) {
+
+            /* found !ENTITY element */
+            pnew = mxml_add_special_node(ptree, ENTITY_NODE, "ENTYTY", NULL);
+            pv = p+1;
+
+            p++;
+            if (strstr(p, ">") == NULL)
+               return read_error(HERE, "Unterminated !ENTITY element");
+
+            while (*p != '>') {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+
+            len = (size_t)p - (size_t)pv;
+            pnew->value = (char *)malloc(len+1);
+            memcpy(pnew->value, pv, len);
+            pnew->value[len] = 0;
+            mxml_decode(pnew->value);
+
+            p ++;
+
          } else {
             
             /* found normal element */
@@ -1407,6 +1431,249 @@
 
 /*------------------------------------------------------------------*/
 
+PMXML_NODE mxml_parse_entity(char *buf, char *error, int error_size)
+/* parse !ENTYTY entries of XML files and replace with references. Return NULL
+   in case of error, return error description. Optional file_name is used
+   for error reporting if called from mxml_parse_file() */
+{
+   char *p;
+   char *pv;
+   char delimiter;
+   int  i,j,k, line_number;
+   char replacement[1000];
+   char entity_name[256][256];
+   char entity_reference_name[256][256];
+   char *entity_value[256];
+   int  entity_type[256]; /* internal or external */
+   int  nentity;
+   int  fh, length,len;
+   char *buffer;
+   PMXML_NODE root = mxml_create_root_node(); // dummy for 'HERE'
+   char *file_name = NULL; // dummy for 'HERE'
+
+   /* copy string to temporary space */
+   buffer = (char *)malloc(strlen(buf)+1);
+   strcpy(buffer,buf);
+   free(buf);
+
+   p = buffer;
+   line_number = 1;
+   nentity = -1;
+
+   /* search !ENTITY */
+   do {
+      if (*p == '<') {
+
+         /* found new entity */
+         p++;
+         while (*p && isspace(*p)) {
+            if (*p == '\n')
+               line_number++;
+            p++;
+         }
+         if (!*p)
+            return read_error(HERE, "Unexpected end of file");
+
+         if (strncmp(p, "!ENTITY", 7) == 0) {
+
+            /* found entity */
+            nentity++;
+            if(nentity>=1000)
+               return read_error(HERE, "Too much entities");
+
+            pv = p+7;
+            while (*pv == ' ')
+               pv++;
+
+            /* extract entity name */
+            p = pv;
+
+            while (*p && isspace(*p) && *p != '<' && *p != '>') {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+            if (!*p)
+               return read_error(HERE, "Unexpected end of file");
+            if (*p == '<' || *p == '>')
+               return read_error(HERE, "Unexpected \'%c\' inside !ENTITY", *p);
+
+            pv = p;
+            while (*pv && !isspace(*pv) && *pv != '<' && *pv != '>')
+               pv++;
+
+            if (!*pv)
+               return read_error(HERE, "Unexpected end of file");
+            if (*pv == '<'  || *pv == '>' )
+               return read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]);
+
+            len = (size_t)pv - (size_t)p;
+            if (len > (int)sizeof(replacement)-1)
+               len = sizeof(replacement)-1;
+            memcpy(replacement, p, len);
+            replacement[len] = 0;
+            mxml_decode(replacement);
+
+            entity_name[nentity][0] = '&';
+            i = 1;
+            entity_name[nentity][i] = 0;
+            while (*p && !isspace(*p) && *p != '/' && *p != '>' && *p != '<' && i<253)
+               entity_name[nentity][i++] = *p++;
+            entity_name[nentity][i++] = ';';
+            entity_name[nentity][i] = 0;
+
+            if (!*p)
+               return read_error(HERE, "Unexpected end of file");
+            if (*p == '<')
+               return read_error(HERE, "Unexpected \'<\' inside entity \"%s\"", &entity_name[nentity][1]);
+
+            /* extract replacement or SYSTEM*/
+            while (*p && isspace(*p)) {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+            if (!*p)
+               return read_error(HERE, "Unexpected end of file");
+            if (*p == '>')
+               return read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]);
+
+            /* check if SYSTEM */
+            if(strncmp(p, "SYSTEM", 6) == 0){
+               entity_type[nentity] = EXTERNAL_ENTITY;
+               p += 6;
+            }
+            else{
+               entity_type[nentity] = INTERNAL_ENTITY;
+            }
+
+            /* extract replacement */
+            while (*p && isspace(*p)) {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+            if (!*p)
+               return read_error(HERE, "Unexpected end of file");
+            if (*p == '>')
+               return read_error(HERE, "Unexpected \'>\' inside entity \"%s\"", &entity_name[nentity][1]);
+
+            if (*p != '\"' && *p != '\'')
+               return read_error(HERE, "Replacement was not found for entity \"%s\"", &entity_name[nentity][1]);
+            delimiter = *p;
+            p++;
+            if (!*p)
+               return read_error(HERE, "Unexpected end of file");
+            pv = p;
+            while (*pv && *pv != delimiter )
+               pv++;
+
+            if (!*pv)
+               return read_error(HERE, "Unexpected end of file");
+            if (*pv == '<' )
+               return read_error(HERE, "Unexpected \'%c\' inside entity \"%s\"", *pv, &entity_name[nentity][1]);
+
+            len = (size_t)pv - (size_t)p;
+            if (len > (int)sizeof(replacement)-1)
+               len = sizeof(replacement)-1;
+            memcpy(replacement, p, len);
+            replacement[len] = 0;
+
+            if(entity_type[nentity] == EXTERNAL_ENTITY){
+               strcpy(entity_reference_name[nentity],replacement);
+            }
+            else{
+               entity_value[nentity] = (char *)malloc(strlen(replacement));
+               strcpy(entity_value[nentity],replacement);
+            }
+
+            p = pv;
+            while (*p && isspace(*p)) {
+               if (*p == '\n')
+                  line_number++;
+               p++;
+            }
+            if (!*p)
+               return read_error(HERE, "Unexpected end of file");
+         }
+      }
+
+      /* go to next element */
+      while (*p && *p != '<') {
+         if (*p == '\n')
+            line_number++;
+         p++;
+      }
+   } while (*p);
+   nentity++;
+
+   /* read external file */
+   for(i=0;i<nentity;i++){
+      if(entity_type[i] == EXTERNAL_ENTITY){
+         fh = open(entity_reference_name[i], O_RDONLY | O_TEXT, 0644);
+
+         if (fh == -1) {
+            return read_error(HERE, "Unable to open file \"%s\"",entity_reference_name[i]);
+         }
+
+         length = lseek(fh, 0, SEEK_END);
+         lseek(fh, 0, SEEK_SET);
+         entity_value[i] = (char *)malloc(length+1);
+         if (entity_value[i] == NULL) {
+            close(fh);
+            return read_error(HERE, "Cannot allocate buffer of %d bytes for \"%s\".",length+1,&entity_name[1]);
+         }
+
+         /* read complete file at once */
+         length = read(fh, entity_value[i], length);
+         entity_value[i][length] = 0;
+         close(fh);
+      }
+   }
+
+   /* count length of output string */
+   length = strlen(buffer);
+   for(i=0;i<nentity;i++){
+      p = buffer;
+      while(1){
+         pv =strstr(entity_name[i],p);
+         if(pv){
+            length += -strlen(entity_name[i]) + strlen(entity_value[i]);
+            p = pv+1;
+         }
+         else{
+            break;
+         }
+      }
+   }
+
+   /* allocate memory */
+   buf = (char *)malloc(length+1);
+
+   /* replace entities */
+   p = buffer;
+   pv = buf;
+   do {
+      if (*p == '&') {
+         /* found entity */
+         for(j=0;j<nentity;j++){
+            if(strncmp(p,entity_name[j],strlen(entity_name[j])) == 0){
+               for(k=0;k<(int)strlen(entity_value[j]);k++)
+                  *pv++ = entity_value[j][k];
+               p += strlen(entity_name[j]);
+               break;
+            }
... 27 more lines ...
ELOG V3.1.4-2e1708b5