Index: /Evidence/Config.cc
===================================================================
--- /Evidence/Config.cc	(revision 202)
+++ /Evidence/Config.cc	(revision 203)
@@ -3,14 +3,15 @@
   Configuration server for the Evidence Control System
 
-  - The configuration file is opened without buffering to catch changes
-    without closing/opening.
   - The name of a configuration file can be given as command line argument
   - If a configuration file change is detected through inotify, the service
-    "Config/ModifyTime" is updated with the current UNIX time to inform applications.
-	The initial value of the server is the last file modification time. 
-  - The employed line buffer has conservatively at least the size of the
-    configuration file. If needed, it will be enlarged.
+    "Config/ModifyTime" contains the last modification UNIX time.
+  - The contents of the configuration file is available through Config/ConfigData.
+  - The current parser removes all tabs, multiple, leading and trailing spaces, and
+    concatenates lines ending with '+'.
+
+  A mutex is used for preventing concurrent access to the configuration data from
+  the methods rpcHandler() and ConfigChanged().
          
-  Oliver Grimm, November 2009
+  Oliver Grimm, April 2010
 
 \********************************************************************/
@@ -24,4 +25,7 @@
 #include <sys/stat.h>
 #include <sys/inotify.h>
+#include <sstream>
+
+using namespace std;
 
 //
@@ -31,9 +35,15 @@
 
   private:
+	struct Item {
+	  string Name;
+	  string Data;
+	};
+	vector<struct Item> List;
+
     FILE *File;	
-    char *Buffer;
-    unsigned int BufferLength;
+	char *FileContent;
     DimService *ConfigModified;
-    int ModifyTime;
+	DimService *ConfigContent;
+    pthread_mutex_t Mutex;
 
     void rpcHandler();
@@ -51,4 +61,13 @@
 	DimRpc("ConfigRequest", "C", "C"), EvidenceServer(SERVER_NAME) {
 
+  ConfigModified = NULL;	// Will be allocated in ConfigChanged()
+  ConfigContent = NULL;
+  FileContent = NULL;
+
+  // Initialise mutex (errno is not set by pthread_mutex_init())
+  if (pthread_mutex_init(&Mutex, NULL) != 0) {
+    State(FATAL, "pthread_mutex_init() failed");
+  }
+
   // Open configuration file
   if ((File = fopen(Filename, "r")) == NULL) {
@@ -56,20 +75,11 @@
   }
 
-  // Create DIM service to indicate changes of configuration file
-  struct stat Stat;
-  if (stat(Filename, &Stat) == -1) {
-    State(WARN, "Could not read last modification time of configuration file '%s' (%s)", Filename, strerror(errno));
-	ModifyTime = 0;
-  }
-  else ModifyTime = Stat.st_mtime;   
-  ConfigModified = new DimService (SERVER_NAME"/ModifyTime", ModifyTime);
-
   // Disable buffering, so file modifications are immediately seen
   if (setvbuf(File, NULL, _IONBF, 0) != 0) {
     State(WARN, "Error setting configuration file '%s' to unbuffered mode", Filename);
   }
-    
-  Buffer = NULL;    // Will be allocated in rpcHandler()
-  BufferLength = 0;
+  
+  // Create DIM services
+  ConfigChanged();
 }
 
@@ -77,68 +87,129 @@
 EvidenceConfig::~EvidenceConfig() {
 
-  if (File != NULL) fclose(File);  
-  delete[] Buffer;
-}
+  if (File != NULL && fclose(File) != 0) State(ERROR, "Error closing configuration file (%s)", strerror(errno));
+
+  delete ConfigModified;
+  delete ConfigContent;
+  delete[] FileContent;
+
+  if (pthread_mutex_destroy(&Mutex) != 0) State(ERROR, "pthread_mutex_destroy() failed");
+}
+
 
 // Implementation of response to configuration request
 void EvidenceConfig::rpcHandler() {
 
-  char *Token1,*Token2,*Token3, *Request = getString();
-  struct stat FileStatus;
-  
-  // Check if Buffer[] is large enough to hold full file, enlarge if necessary
-  if (fstat(fileno(File), &FileStatus) == -1) {
-     State(FATAL, "Could not determine size of configuration file to allocate buffer (%s)", strerror(errno));
-  }
-  else if(BufferLength < FileStatus.st_size) {
-    delete[] Buffer;
-    Buffer = new char [FileStatus.st_size];
-    BufferLength = FileStatus.st_size;   
-  }
-  
-  // Search for configuration item
-  rewind(File);
-  while (fgets(Buffer, BufferLength, File) != NULL) {
+  string Response;
+  
+  // Lock because ConfigChange() might access concurrently
+  if (pthread_mutex_lock(&Mutex) != 0) State(ERROR, "pthread_mutex_lock() failed in rpcHandler()");
+
+  // Search for config data in list
+  for (int i=0; i<List.size(); i++) {
+    if (List[i].Name == getString()) Response = List[i].Data;
+  }
+  
+  // Unlock
+  if (pthread_mutex_unlock(&Mutex) != 0) State(ERROR, "pthread_mutex_unlock() failed in rpcHandler()");
+
+  // Send data and update Status
+  setData((char *) Response.c_str());
     
-    // Combine lines that end with '+'
-    while (Buffer[strlen(Buffer)-2] == '+') {
-      if (fgets(Buffer+strlen(Buffer)-2, BufferLength-(strlen(Buffer)-2), File) == NULL) break;
-    }
-    
-    // Ignore comments
-    for (int i=0; i<strlen(Buffer); i++) if (Buffer[i] == '#') Buffer[i] = '\0';
-    
-    // Extract tokens
-    Token1 = strtok(Buffer, " \t:");
-    Token2 = strtok(NULL, " \t:");
-    Token3 = strtok(NULL, "\n");
-    
-    // Check if all tokens existing
-    if(Token1==NULL || Token2==NULL || Token3==NULL) continue;
-
-    // Check for match and then send data (removing whitespace and both ends)
-    if (strstr(Request, Token1)!=NULL && strstr(Request, Token2)!=NULL) {
-	  while (isspace(*Token3) != 0) Token3++;
-	  while ((strlen(Token3)>0) && isspace(*(Token3+strlen(Token3)-1))) *(Token3+strlen(Token3)-1) = '\0';
-      setData(Token3);
-      break;
-    }
-  }
-  
-  // If configuration data not found, send empty string
-  if (feof(File)!=0) setData((char *) "");
-  
   State(INFO, "Client '%s' (ID %d) requested '%s'. Send '%s'.",
 		DimServer::getClientName(),
 		DimServer::getClientId(), 
-		Request, feof(File)!=0 ? "n/a" : Token3);
-}
+		getString(), Response.c_str());
+}
+
 
 // Signalisation of configuration change
 void EvidenceConfig::ConfigChanged() {
 
-  ModifyTime = time(NULL);
-  ConfigModified->updateService();
-}
+  static int ModifyTime;
+
+  //
+  // Part A: Handle updates to DIM services
+  //
+  
+  // Access status information for configuration file (if fileno() returns -1, fstat() reports error)
+  struct stat Stat;
+  if (fstat(fileno(File), &Stat) == -1) {
+    State(ERROR, "Error with stat() system call for configuration file, cannot update configuration file information (%s)", strerror(errno));
+	delete ConfigModified;
+	delete ConfigContent;
+	ConfigModified = NULL;
+	ConfigContent = NULL;
+	return;
+  }
+
+  // Create/update DIM service to indicate changes of configuration file
+  ModifyTime = Stat.st_mtime;   
+  if (ConfigModified == NULL) ConfigModified = new DimService (SERVER_NAME"/ModifyTime", ModifyTime);
+  else ConfigModified->updateService();
+  
+  // Read new configuration file (enfore \0 termination)
+  char *NewContent = new char [Stat.st_size+1];
+  rewind(File);
+  if (fread(NewContent, sizeof(char), Stat.st_size, File) != Stat.st_size) {
+    State(FATAL, "Could not read configuration file");
+  }
+  NewContent[Stat.st_size] = '\0';
+
+  // Create/update DIM service that contains the current configuration file
+  if (ConfigContent == NULL )ConfigContent = new DimService(SERVER_NAME"/ConfigData", NewContent);
+  else ConfigContent->updateService(NewContent);
+
+  delete[] FileContent;
+  FileContent = NewContent;
+
+  //
+  // Part B: Interpret configuration list
+  //
+
+  stringstream In(FileContent), Out;
+  string Line;
+  struct Item New;
+  size_t Pos;
+  
+  // First clean up and concatenate lines
+  while (getline(In, Line).good()) {
+    // Remove comments
+	if (Line.find('#') != string::npos) Line.erase(Line.find('#'));
+    // Repace all tabs by spaces
+    while (Line.find("\t") != string::npos) Line[Line.find("\t")] = ' ';
+	// Remove leading spaces
+	while (!Line.empty() && isspace(Line[0])) Line.erase(0, 1);
+	// Remove trailing spaces
+	while (!Line.empty() && isspace(Line[Line.size()-1])) Line.erase(Line.size()-1);
+	// Remove empty lines
+	if (Line.empty()) continue;
+	// Concatenate if line ends with '+'
+    if (Line[Line.size()-1] != '+') Out << Line << endl;
+	else Out << Line.erase(Line.size()-1);
+  };
+  
+  In.str(Out.str());
+  In.clear();
+
+  // Interpret data
+  while (getline(In, Line).good()) {
+    // Remove multiple spaces
+    while (Line.find("  ") != string::npos) Line.erase(Line.find("  "), 1);
+
+	// Find second space character
+    Pos = Line.find(" ", Line.find(" ") + 1);
+	if(Pos == string::npos) continue;
+
+	// Extract configuration name and data
+	New.Name = string(Line, 0, Pos);
+	New.Data = string(Line, Pos+1);
+
+	// Add to configuration list
+	if (pthread_mutex_lock(&Mutex) != 0) State(ERROR, "pthread_mutex_lock() failed in ConfigChanged()");
+	List.push_back(New);
+	if (pthread_mutex_unlock(&Mutex) != 0) State(ERROR, "pthread_mutex_unlock() failed in ConfigChanged()");
+  };
+}
+
 
 //	    
Index: /Evidence/DColl.cc
===================================================================
--- /Evidence/DColl.cc	(revision 202)
+++ /Evidence/DColl.cc	(revision 203)
@@ -319,6 +319,9 @@
 
 	if (Text != NULL) {
-	  // Replace all control characters by white space
-	  for (int i=0; i<strlen(Text); i++) if (iscntrl(Text[i])) Text[i] = ' ';
+	  // Replace new line by '\' and all other control characters by white space
+	  for (int i=0; i<strlen(Text); i++) {
+		if (Text[i] == '\n') Text[i] = '\\';
+		else if (iscntrl(Text[i])) Text[i] = ' ';
+	  }
 
 	  // Write to file
