/***************************************************************************

  PRunDataHandler.cpp

  Author: Andreas Suter
  e-mail: andreas.suter@psi.ch

  $Id$

***************************************************************************/

/***************************************************************************
 *   Copyright (C) 2007 by Andreas Suter                                   *
 *   andreas.suter@psi.ch                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <cstdio>
#include <ctime>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

#include <TROOT.h>
#include <TSystem.h>
#include <TString.h>
#include <TObjArray.h>
#include <TObjString.h>
#include <TFile.h>
#include <TFolder.h>
#include <TH1F.h>
#include <TDatime.h>

#include "TLemRunHeader.h"
#include "MuSR_td_PSI_bin.h"
#include "mud.h"

#ifdef PNEXUS_ENABLED
#include "PNeXus.h"
#endif

#include "PRunDataHandler.h"

#define ROOT_ALL 0
#define ROOT_NPP 1
#define ROOT_PPC 2

#define A2M_UNDEFINED  0
#define A2M_ROOT       1
#define A2M_PSIBIN     2
#define A2M_PSIMDU     3
#define A2M_MUD        4
#define A2M_NEXUS      5
#define A2M_WKM        6
#define A2M_ASCII      7

//--------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
/**
 * <p>Constructor, reading the data histogramm files.
 *
 * \param msrInfo pointer to the msr-file handler
 */
PRunDataHandler::PRunDataHandler(PAny2ManyInfo *any2ManyInfo) : fAny2ManyInfo(any2ManyInfo)
{
  fDataPath.clear();
  // read files
  if (!ReadWriteFilesList()) // couldn't read file
    fAllDataAvailable = false;
  else
    fAllDataAvailable = true;
}

//--------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
/**
 * <p>Constructor, reading the data histogramm files.
 *
 * \param msrInfo pointer to the msr-file handler
 */
PRunDataHandler::PRunDataHandler(PAny2ManyInfo *any2ManyInfo, const PStringVector dataPath) :
    fAny2ManyInfo(any2ManyInfo), fDataPath(dataPath)
{
  // read files
  if (!ReadWriteFilesList()) // couldn't read file
    fAllDataAvailable = false;
  else
    fAllDataAvailable = true;
}

//--------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
/**
 * <p>Constructor, reading the data histogramm files.
 *
 * \param msrInfo pointer to the msr-file handler
 */
PRunDataHandler::PRunDataHandler(PMsrHandler *msrInfo) : fMsrInfo(msrInfo)
{
  // read files
  if (!ReadFilesMsr()) // couldn't read file
    fAllDataAvailable = false;
  else
    fAllDataAvailable = true;
}

//--------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------
/**
 * <p> Constructor, reading the data histogramm files, and keeping a copy
 * of potential search paths.
 *
 * \param msrInfo pointer to the msr-file handler
 * \param dataPath vector containing search paths where to find raw data files.
 */
PRunDataHandler::PRunDataHandler(PMsrHandler *msrInfo, const PStringVector dataPath) : 
      fMsrInfo(msrInfo), fDataPath(dataPath)
{
  // read files
  if (!ReadFilesMsr()) // couldn't read file
    fAllDataAvailable = false;
  else
    fAllDataAvailable = true;
}

//--------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------
/**
 * <p> Destructor.
 */
PRunDataHandler::~PRunDataHandler()
{
  fData.clear();
}

//--------------------------------------------------------------------------
// GetRunData
//--------------------------------------------------------------------------
/**
 * <p>Checks if runName is found.
 *
 * <b>return:</b>
 * - if data are found: pointer to the data.
 * - otherwise the null pointer will be returned.
 *
 * \param runName run name, e.g. 2009/lem09_his_1234
 */
PRawRunData* PRunDataHandler::GetRunData(const TString &runName)
{
  UInt_t i;

  for (i=0; i<fData.size(); i++) {
    if (!fData[i].GetRunName()->CompareTo(runName)) // run found
      break;
  }

  if (i == fData.size())
    return 0;
  else
    return &fData[i];
}

//--------------------------------------------------------------------------
// ReadFilesMsr
//--------------------------------------------------------------------------
/**
 * <p> The main read file routine which is filtering what read sub-routine
 * needs to be called. Called when the input is a msr-file.
 *
 * <b>return:</b>
 * - true if reading was successful,
 * - false if reading failed.
 */
Bool_t PRunDataHandler::ReadFilesMsr()
{
  Bool_t success = true;

  // loop over the full RUN list to see what needs to be read
  PMsrRunList *runList = 0;
  runList = fMsrInfo->GetMsrRunList();
  if (runList == 0) {
    cerr << endl << ">> PRunDataHandler::ReadFilesMsr(): **ERROR** Couldn't obtain run list from PMsrHandler: something VERY fishy";
    cerr << endl;
    return false;
  }

  for (UInt_t i=0; i<runList->size(); i++) {
    for (UInt_t j=0; j<runList->at(i).GetRunNameSize(); j++) {
      fRunName = *(runList->at(i).GetRunName(j));
      // check is file is already read
      if (FileAlreadyRead(*(runList->at(i).GetRunName(j))))
        continue;
      // check if file actually exists
      if (!FileExistsCheck(runList->at(i), j))
        return false;
      // everything looks fine, hence try to read the data file
      if (!runList->at(i).GetFileFormat(j)->CompareTo("root-npp")) // not post pile up corrected histos
        success = ReadRootFile(ROOT_NPP);
      else if (!runList->at(i).GetFileFormat(j)->CompareTo("root-ppc")) // post pile up corrected histos
        success = ReadRootFile(ROOT_PPC);
      else if (!runList->at(i).GetFileFormat(j)->CompareTo("nexus"))
        success = ReadNexusFile();
      else if (!runList->at(i).GetFileFormat(j)->CompareTo("psi-bin"))
        success = ReadPsiBinFile();
      else if (!runList->at(i).GetFileFormat(j)->CompareTo("psi-mdu"))
        success = ReadPsiBinFile();
      else if (!runList->at(i).GetFileFormat(j)->CompareTo("mud"))
        success = ReadMudFile();
      else if (!runList->at(i).GetFileFormat(j)->CompareTo("wkm"))
        success = ReadWkmFile();
      else if (!runList->at(i).GetFileFormat(j)->CompareTo("mdu-ascii"))
        success = ReadMduAsciiFile();        
      else if (!runList->at(i).GetFileFormat(j)->CompareTo("ascii"))
        success = ReadAsciiFile();
      else if (!runList->at(i).GetFileFormat(j)->CompareTo("db"))
        success = ReadDBFile();
      else
        success = false;
    }
  }

  return success;
}

//--------------------------------------------------------------------------
// ReadWriteFilesList
//--------------------------------------------------------------------------
/**
 * <p> The main read file routine which is filtering what read sub-routine
 * needs to be called. Called when the input is a list of runs. Used with
 * any2many. If the input data file is successfully read, it will write the
 * converted data file.
 *
 * <b>return:</b>
 * - true if reading was successful,
 * - false if reading failed.
 */
Bool_t PRunDataHandler::ReadWriteFilesList()
{
  if ((fAny2ManyInfo->inFileName.size() == 0) && (fAny2ManyInfo->runList.size() == 0)) {
    cerr << endl << ">> PRunDataHandler::ReadWriteFilesList(): **ERROR** Couldn't obtain run list from fAny2ManyInfo: something VERY fishy";
    cerr << endl;
    return false;
  }

  // get input file format tag
  Int_t inTag = A2M_UNDEFINED;
  if (!fAny2ManyInfo->inFormat.CompareTo("root", TString::kIgnoreCase))
    inTag = A2M_ROOT;
  else if (!fAny2ManyInfo->inFormat.CompareTo("psi-bin", TString::kIgnoreCase))
    inTag = A2M_PSIBIN;
  else if (!fAny2ManyInfo->inFormat.CompareTo("psi-mdu", TString::kIgnoreCase))
    inTag = A2M_PSIMDU;
  else if (!fAny2ManyInfo->inFormat.CompareTo("mud", TString::kIgnoreCase))
    inTag = A2M_MUD;
  else if (!fAny2ManyInfo->inFormat.CompareTo("nexus", TString::kIgnoreCase))
    inTag = A2M_NEXUS;
  else if (!fAny2ManyInfo->inFormat.CompareTo("wkm", TString::kIgnoreCase))
    inTag = A2M_WKM;
  else
    inTag = A2M_UNDEFINED;

  if (inTag == A2M_UNDEFINED) {
    cerr << endl << ">> PRunDataHandler::ReadWriteFilesList(): **ERROR** no valid input data file format found: '" << fAny2ManyInfo->inFormat.Data() << "'" << endl;
    return false;
  }

  // get output file format tag
  Int_t outTag = A2M_UNDEFINED;
  if (!fAny2ManyInfo->outFormat.CompareTo("root", TString::kIgnoreCase))
    outTag = A2M_ROOT;
  else if (!fAny2ManyInfo->outFormat.CompareTo("psi-bin", TString::kIgnoreCase))
    outTag = A2M_PSIBIN;
  else if (!fAny2ManyInfo->outFormat.CompareTo("mud",TString::kIgnoreCase))
    outTag = A2M_MUD;
  else if (fAny2ManyInfo->outFormat.BeginsWith("nexus", TString::kIgnoreCase))
    outTag = A2M_NEXUS;
  else if (!fAny2ManyInfo->outFormat.CompareTo("wkm", TString::kIgnoreCase))
    outTag = A2M_WKM;
  else if (!fAny2ManyInfo->outFormat.CompareTo("ascii", TString::kIgnoreCase))
    outTag = A2M_ASCII;
  else
    outTag = A2M_UNDEFINED;

  if (outTag == A2M_UNDEFINED) {
    cerr << endl << ">> PRunDataHandler::ReadWriteFilesList(): **ERROR** no valid output data file format found: '" << fAny2ManyInfo->outFormat.Data() << "'" << endl;
    return false;
  }

  if (fAny2ManyInfo->inFileName.size() != 0) { // file name list given

    // loop over all runs of the run list
    for (UInt_t i=0; i<fAny2ManyInfo->inFileName.size(); i++) {
      if (!FileExistsCheck(true, i)) {
        cerr << endl << ">> PRunDataHandler::ReadWriteFilesList(): **ERROR** Couldn't find file " << fAny2ManyInfo->inFileName[i].Data() << endl;
        return false;
      }

      // read input file
      Bool_t success = false;
      switch (inTag) {
      case A2M_ROOT:
        success = ReadRootFile(ROOT_ALL);
        break;
      case A2M_PSIBIN:
        success = ReadPsiBinFile();
        break;
      case A2M_PSIMDU:
        success = ReadPsiBinFile();
        break;
      case A2M_NEXUS:
        success = ReadNexusFile();
        break;
      case A2M_MUD:
        success = ReadMudFile();
        break;
      case A2M_WKM:
        success = ReadWkmFile();
        break;
      default:
        break;
      }

      if (!success) {
        cerr << endl << ">> PRunDataHandler::ReadWriteFilesList(): **ERROR** Couldn't read file " << fAny2ManyInfo->inFileName[i].Data() << endl;
        return false;
      }

      // write 'converted' output data file
      success = false;
      switch (outTag) {
      case A2M_ROOT:
        success = WriteRootFile();
        break;
      case A2M_PSIBIN:
        success = WritePsiBinFile();
        break;
      case A2M_PSIMDU:
        success = WritePsiBinFile();
        break;
      case A2M_NEXUS:
        success = WriteNexusFile();
        break;
      case A2M_MUD:
        success = WriteMudFile();
        break;
      case A2M_WKM:
        success = WriteWkmFile();
        break;
      case A2M_ASCII:
        success = WriteAsciiFile();
        break;
      default:
        break;
      }

      if (success == false) {
        cerr << endl << ">> PRunDataHandler::ReadWriteFilesList(): **ERROR** Couldn't write converted output file." << endl;
        return false;
      }
    }
  }

  if (fAny2ManyInfo->runList.size() != 0) { // run list given

    // loop over all runs of the run list
    for (UInt_t i=0; i<fAny2ManyInfo->runList.size(); i++) {
      if (!FileExistsCheck(false, i)) {
        cerr << endl << ">> PRunDataHandler::ReadWriteFilesList(): **ERROR** Couldn't find run " << fAny2ManyInfo->runList[i] << endl;
        return false;
      }

      // read input file
      Bool_t success = false;
      switch (inTag) {
      case A2M_ROOT:
        success = ReadRootFile(ROOT_ALL);
        break;
      case A2M_PSIBIN:
        success = ReadPsiBinFile();
        break;
      case A2M_PSIMDU:
        success = ReadPsiBinFile();
        break;
      case A2M_NEXUS:
        success = ReadNexusFile();
        break;
      case A2M_MUD:
        success = ReadMudFile();
        break;
      case A2M_WKM:
        success = ReadWkmFile();
        break;
      default:
        break;
      }

      if (!success) {
        cerr << endl << ">> PRunDataHandler::ReadWriteFilesList(): **ERROR** Couldn't read file " << fRunPathName.Data() << endl;
        return false;
      }

      TString year("");
      TDatime dt;
      year += dt.GetYear();
      if (fAny2ManyInfo->year.Length() > 0)
        year = fAny2ManyInfo->year;
      Bool_t ok;
      TString fln = FileNameFromTemplate(fAny2ManyInfo->outTemplate, fAny2ManyInfo->runList[i], year, ok);
      if (!ok) {
        cerr << endl << ">> PRunDataHandler::ReadWriteFilesList(): **ERROR** Couldn't create necessary output file name." << endl;
        return false;
      }

      // write 'converted' output data file
      success = false;
      switch (outTag) {
      case A2M_ROOT:
        success = WriteRootFile(fln);
        break;
      case A2M_PSIBIN:
        success = WritePsiBinFile(fln);
        break;
      case A2M_PSIMDU:
        success = WritePsiBinFile(fln);
        break;
      case A2M_NEXUS:
        success = WriteNexusFile(fln);
        break;
      case A2M_MUD:
        success = WriteMudFile(fln);
        break;
      case A2M_WKM:
        success = WriteWkmFile(fln);
        break;
      case A2M_ASCII:
        success = WriteAsciiFile(fln);
        break;
      default:
        break;
      }

      if (success == false) {
        cerr << endl << ">> PRunDataHandler::ReadWriteFilesList(): **ERROR** Couldn't write converted output file." << endl;
        return false;
      }

      // throw away the current data set
      fData.clear();
    }

  }

  // check if compression is wished
  if (fAny2ManyInfo->compressionTag > 0) {
    TString fln = fAny2ManyInfo->compressFileName;

    // currently system call is used, which means this is only running under Linux and Mac OS X but not under Windows
    char cmd[256];
    if (fAny2ManyInfo->outPathFileName.size() == 1) {
      if (fAny2ManyInfo->compressionTag == 1) // gzip
        fln += TString(".tar.gz");
      else // bzip2
        fln += TString(".tar.bz2");
      if (fAny2ManyInfo->compressionTag == 1) // gzip
        sprintf(cmd, "tar -zcf %s %s", fln.Data(), fAny2ManyInfo->outPathFileName[0].Data());
      else // bzip2
        sprintf(cmd, "tar -jcf %s %s", fln.Data(), fAny2ManyInfo->outPathFileName[0].Data());
      system(cmd);
    } else {
      fln += TString(".tar");
      for (UInt_t i=0; i<fAny2ManyInfo->outPathFileName.size(); i++) {
        if (i==0) {
          sprintf(cmd, "tar -cf %s %s", fln.Data(), fAny2ManyInfo->outPathFileName[i].Data());
        } else {
          sprintf(cmd, "tar -rf %s %s", fln.Data(), fAny2ManyInfo->outPathFileName[i].Data());
        }
        system(cmd);
      }
      if (fAny2ManyInfo->compressionTag == 1) { // gzip
        sprintf(cmd, "gzip %s", fln.Data());
        fln += ".gz";
      } else {
        sprintf(cmd, "bzip2 -z %s", fln.Data());
        fln += ".bz2";
      }
      system(cmd);
    }

    // check if the compressed file shall be streamed to the stdout
    if (fAny2ManyInfo->useStandardOutput) {
      // stream file to stdout
      ifstream is;
      int length=1024;
      char *buffer;

      is.open(fln.Data(), ios::binary);
      if (!is.is_open()) {
        cerr << endl << "PRunDataHandler::ReadWriteFilesList(): **ERROR** Couldn't open the file for streaming." << endl;
        remove(fln.Data());
        return false;
      }

      // get length of file
      is.seekg(0, ios::end);
      length = is.tellg();
      is.seekg(0, ios::beg);

      if (length == -1) {
        cerr << endl << "PRunDataHandler::ReadWriteFilesList(): **ERROR** Couldn't determine the file size." << endl;
        remove(fln.Data());
        return false;
      }

      // allocate memory
      buffer = new char [length];

      // read data as a block
      while (!is.eof()) {
        is.read(buffer, length);
        cout.write(buffer, length);
      }

      is.close();

      delete [] buffer;

      // delete temporary root file
      remove(fln.Data());
    }

    // remove all the converted files
    for (UInt_t i=0; i<fAny2ManyInfo->outPathFileName.size(); i++)
      remove(fAny2ManyInfo->outPathFileName[i].Data());
  }

  return true;
}

//--------------------------------------------------------------------------
// FileAlreadyRead
//--------------------------------------------------------------------------
/**
 * <p> Checks if a file has been already read in order to prevent multiple
 * reading of data files.
 *
 * <b>return:</b>
 * - true if the file has been read before,
 * - otherwise false.
 *
 * \param runName run name to be check if the corresponding file is already read.
 */
Bool_t PRunDataHandler::FileAlreadyRead(TString runName)
{
  for (UInt_t i=0; i<fData.size(); i++) {
    if (!fData[i].GetRunName()->CompareTo(runName)) { // run alread read
      return true;
    }
  }

  return false;
}

//--------------------------------------------------------------------------
// TestFileName -- private
//--------------------------------------------------------------------------
/**
 * <p> Tests if a file exists (with or without given extension).
 *     The given file-name string will be modified to show the found file name or an empty string if no file is found.
 *
 * \param runName run name with full path to be checked
 * \param ext extension to be checked
 */
void PRunDataHandler::TestFileName(TString &runName, const TString &ext)
{
  TString tmpStr(runName), tmpExt(ext);

  // check first if the file exists with the default extension which is not given with the run name
  runName += TString(".") + ext;
  if (gSystem->AccessPathName(runName.Data()) != true) { // found
    return;
  }

  // test if the file exists with only upper-case letters
  runName.ToUpper();
  if (gSystem->AccessPathName(runName.Data()) != true) { // found
    return;
  }

  // test if the file exists with only lower-case letters
  runName.ToLower();
  if (gSystem->AccessPathName(runName.Data()) != true) { // found
    return;
  }

  // test if the file exists with the given run name but an only upper-case extension which is not included in the file name
  runName = tmpStr;
  tmpExt.ToUpper();
  runName += TString(".") + tmpExt;
  if (gSystem->AccessPathName(runName.Data()) != true) { // found
    return;
  }

  // test if the file exists with the given run name but an only lower-case extension which is not included in the file name
  tmpExt.ToLower();
  runName = runName.Replace(static_cast<Ssiz_t>(runName.Length()-tmpExt.Length()), tmpExt.Length(), tmpExt);
  if (gSystem->AccessPathName(runName.Data()) != true) { // found
    return;
  }

  runName = tmpStr;

  // the extension is already part of the file name, therefore, do not append it
  if (runName.EndsWith(ext.Data(), TString::kIgnoreCase)) {
    if (gSystem->AccessPathName(runName.Data()) != true) { // found
      return;
    }

    // assume some extension is part of the given file name but the real data file ends with an lower-case extension
    tmpExt.ToLower();
    runName = runName.Replace(static_cast<Ssiz_t>(runName.Length()-tmpExt.Length()), tmpExt.Length(), tmpExt);
    if (gSystem->AccessPathName(runName.Data()) != true) { // found
      return;
    }

    // assume some extension is part of the given file name but the real data file ends with an upper-case extension
    tmpExt.ToUpper();
    runName = runName.Replace(static_cast<Ssiz_t>(runName.Length()-tmpExt.Length()), tmpExt.Length(), tmpExt);
    if (gSystem->AccessPathName(runName.Data()) != true) { // found
      return;
    }

    // test if the file exists with only lower-case letters and the extension already included in the file name
    runName.ToLower();
    if (gSystem->AccessPathName(runName.Data()) != true) { // found
      return;
    }

    // test if the file exists with only upper-case letters and the extension already included in the file name
    runName.ToUpper();
    if (gSystem->AccessPathName(runName.Data()) != true) { // found
      return;
    }
  }

  // if the file has not been found, set the run name to be empty
  runName = "";
  return;
}

//--------------------------------------------------------------------------
// FileExistsCheck
//--------------------------------------------------------------------------
/**
 * <p>Checks if a given data file exists.
 *
 * <b>return:</b>
 * - true if data file exists,
 * - otherwise false.
 *
 * \param runInfo reference to the msr-run-structure
 * \param idx index of the run (needed for ADDRUN feature).
 */
Bool_t PRunDataHandler::FileExistsCheck(PMsrRunBlock &runInfo, const UInt_t idx)
{
  Bool_t success = true;

  // local init
  TROOT root("PRunBase", "PRunBase", 0);
  TString pathName = "???";
  TString str, *pstr;
  TString ext;

  pstr = runInfo.GetBeamline(idx);
  if (pstr == 0) {
    cerr << endl << ">> PRunDataHandler::FileExistsCheck: **ERROR** Couldn't obtain beamline data." << endl;
    assert(0);
  }
  pstr->ToLower();
  runInfo.SetBeamline(*pstr, idx);
  pstr = runInfo.GetInstitute(idx);
  if (pstr == 0) {
    cerr << endl << ">> PRunDataHandler::FileExistsCheck: **ERROR** Couldn't obtain institute data." << endl;
    assert(0);
  }
  pstr->ToLower();
  runInfo.SetInstitute(*pstr, idx);
  pstr = runInfo.GetFileFormat(idx);
  if (pstr == 0) {
    cerr << endl << ">> PRunDataHandler::FileExistsCheck: **ERROR** Couldn't obtain file format data." << endl;
    assert(0);
  }
  pstr->ToLower();
  runInfo.SetFileFormat(*pstr, idx);

  // file extensions for the various formats
  if (!runInfo.GetFileFormat(idx)->CompareTo("root-npp")) // not post pile up corrected histos
    ext = TString("root");
  else if (!runInfo.GetFileFormat(idx)->CompareTo("root-ppc")) // post pile up corrected histos
    ext = TString("root");
  else if (!runInfo.GetFileFormat(idx)->CompareTo("nexus"))
    ext = TString("NXS");
  else if (!runInfo.GetFileFormat(idx)->CompareTo("psi-bin"))
    ext = TString("bin");
  else if (!runInfo.GetFileFormat(idx)->CompareTo("psi-mdu"))
    ext = TString("mdu");
  else if (!runInfo.GetFileFormat(idx)->CompareTo("mud"))
    ext = TString("msr");
  else if (!runInfo.GetFileFormat(idx)->CompareTo("wkm")) {
    if (!runInfo.GetBeamline(idx)->CompareTo("mue4"))
      ext = TString("nemu");
    else
      ext = *runInfo.GetBeamline(idx);
  }
  else if (!runInfo.GetFileFormat(idx)->CompareTo("mdu-ascii"))
    ext = TString("mdua");
  else if (!runInfo.GetFileFormat(idx)->CompareTo("ascii"))
    ext = TString("dat");
  else if (!runInfo.GetFileFormat(idx)->CompareTo("db"))
    ext = TString("db");
  else
    success = false;

  // unkown file format found
  if (!success) {
    pstr = runInfo.GetFileFormat(idx);
    if (pstr == 0) {
      cerr << endl << ">> PRunDataHandler::FileExistsCheck: **ERROR** Couldn't obtain file format data." << endl;
      assert(0);
    }
    pstr->ToUpper();
    cerr << endl << ">> PRunDataHandler::FileExistsCheck: **ERROR** File Format '" << pstr->Data() << "' unsupported.";
    cerr << endl << ">>   support file formats are:";
    cerr << endl << ">>   ROOT-NPP   -> root not post pileup corrected for lem";
    cerr << endl << ">>   ROOT-PPC   -> root post pileup corrected for lem";
    cerr << endl << ">>   NEXUS      -> nexus file format, HDF4, HDF5, or XML";
    cerr << endl << ">>   PSI-BIN    -> psi bin file format";
    cerr << endl << ">>   PSI-MDU    -> psi mdu file format (see also MDU-ASCII)";
    cerr << endl << ">>   MUD        -> triumf mud file format";
    cerr << endl << ">>   WKM        -> wkm ascii file format";
    cerr << endl << ">>   MDU-ASCII  -> psi mdu ascii file format";
    cerr << endl << ">>   ASCII      -> column like file format";
    cerr << endl << ">>   DB         -> triumf db file \"format\"";
    cerr << endl;
    return success;
  }

  // check if the file is in the local directory
  str = *runInfo.GetRunName(idx);
  TestFileName(str, ext);
  if (!str.IsNull()) {
    pathName = str;
  }

  // check if the file is found in the <msr-file-directory>
  if (pathName.CompareTo("???") == 0) { // not found in local directory search
    str = *fMsrInfo->GetMsrFileDirectoryPath() + *runInfo.GetRunName(idx);
    TestFileName(str, ext);
    if (!str.IsNull()) {
      pathName = str;
    }
  }

  // check if the file is found in the directory given in the startup file
  if (pathName.CompareTo("???") == 0) { // not found in local directory search and not found in the <msr-file-directory> search
    for (UInt_t i=0; i<fDataPath.size(); i++) {
      str = fDataPath[i] + TString("/") + *runInfo.GetRunName(idx);
      TestFileName(str, ext);
      if (!str.IsNull()) {
        pathName = str;
        break;
      }
    }
  }

  // check if the file is found in the directories given by MUSRFULLDATAPATH
  const Char_t *musrpath = gSystem->Getenv("MUSRFULLDATAPATH");
  if (pathName.CompareTo("???") == 0) { // not found in local directory and xml path
    str = TString(musrpath);
    // MUSRFULLDATAPATH has the structure: path_1:path_2:...:path_n
    TObjArray *tokens = str.Tokenize(":");
    TObjString *ostr;
    for (Int_t i=0; i<tokens->GetEntries(); i++) {
      ostr = dynamic_cast<TObjString*>(tokens->At(i));
      str = ostr->GetString() + TString("/") + *runInfo.GetRunName(idx);
      TestFileName(str, ext);
      if (!str.IsNull()) {
        pathName = str;
        break;
      }
    }
    // cleanup
    if (tokens) {
      delete tokens;
      tokens = 0;
    }
  }

  // check if the file is found in the generated default path
  if (pathName.CompareTo("???") == 0) { // not found in MUSRFULLDATAPATH search
    str = TString(musrpath);
    // MUSRFULLDATAPATH has the structure: path_1:path_2:...:path_n
    TObjArray *tokens = str.Tokenize(":");
    TObjString *ostr;
    pstr = runInfo.GetInstitute(idx);
    if (pstr == 0) {
      cerr << endl << ">> PRunDataHandler::FileExistsCheck: **ERROR** Couldn't obtain institute data." << endl;
      assert(0);
    }
    pstr->ToUpper();
    runInfo.SetInstitute(*pstr, idx);
    pstr = runInfo.GetBeamline(idx);
    if (pstr == 0) {
      cerr << endl << ">> PRunDataHandler::FileExistsCheck: **ERROR** Couldn't obtain beamline data." << endl;
      assert(0);
    }
    pstr->ToUpper();
    runInfo.SetBeamline(*pstr, idx);
    TDatime datetime;
    TString dt;
    dt += datetime.GetYear();
    for (Int_t i=0; i<tokens->GetEntries(); i++) {
      ostr = dynamic_cast<TObjString*>(tokens->At(i));
      str = ostr->GetString() + TString("/DATA/") +
            *runInfo.GetInstitute(idx) + TString("/") +
            *runInfo.GetBeamline(idx) + TString("/") +
            dt + TString("/") +
            *runInfo.GetRunName(idx);
      TestFileName(str, ext);
      if (!str.IsNull()) { // found
        pathName = str;
        break;
      }
    }
    // clean up
    if (tokens) {
      delete tokens;
      tokens = 0;
    }
  }

  // no proper path name found
  if (pathName.CompareTo("???") == 0) {
    cerr << endl << ">> PRunDataHandler::FileExistsCheck(): **ERROR** Couldn't find '" << runInfo.GetRunName(idx)->Data() << "' in any standard path.";
    cerr << endl << ">>  standard search pathes are:";
    cerr << endl << ">>  1. the local directory";
    cerr << endl << ">>  2. the data directory given in the startup XML file";
    cerr << endl << ">>  3. the directories listed in MUSRFULLDATAPATH";
    cerr << endl << ">>  4. default path construct which is described in the manual";
    return false;
  }

  fRunPathName = pathName;

  return true;
}

//--------------------------------------------------------------------------
// FileExistsCheck
//--------------------------------------------------------------------------
/**
 * <p>Checks if a given data file exists. Used for the any2many program.
 *
 * <b>return:</b>
 * - true if data file exists,
 * - otherwise false.
 *
 * \param fileName flag showing if a file name shall be handled or a run number
 * \param idx index of the run. idx == -1 means that a single input data file name is given.
 */
Bool_t PRunDataHandler::FileExistsCheck(const Bool_t fileName, const Int_t idx)
{
  TString pathName("???");
  TString str("");
  TString fln("");

  if (fileName) { // single input file name
    if (idx >= (Int_t)fAny2ManyInfo->inFileName.size()) {
      cerr << endl << ">> PRunDataHandler::FileExistsCheck(): **ERROR** idx=" << idx << " out of range. (inFileName.size()==" << fAny2ManyInfo->inFileName.size() << ")" << endl;
      return false;
    }
    fln = fAny2ManyInfo->inFileName[idx];
  } else { // run file list entry shall be handled
    if (idx >= (Int_t)fAny2ManyInfo->runList.size()) {
      cerr << endl << ">> PRunDataHandler::FileExistsCheck(): **ERROR** idx=" << idx << " out of range. (inFileName.size()==" << fAny2ManyInfo->runList.size() << ")" << endl;
      return false;
    }
    // check for input/output templates
    if ((fAny2ManyInfo->inTemplate.Length() == 0) || (fAny2ManyInfo->outTemplate.Length() == 0)) {
      cerr << endl << ">> PRunDataHandler::FileExistsCheck(): **ERROR** when using run lists, input/output templates are needed as well." << endl;
      return false;
    }
    // make the input file name according to the input template
    TString year("");
    TDatime dt;
    year += dt.GetYear();
    if (fAny2ManyInfo->year.Length() > 0)
      year = fAny2ManyInfo->year;
    Bool_t ok;
    fln = FileNameFromTemplate(fAny2ManyInfo->inTemplate, fAny2ManyInfo->runList[idx], year, ok);
    if (!ok)
      return false;
  }

  // check if the file is in the local directory
  if (gSystem->AccessPathName(fln) != true) { // found in the local dir
    pathName = fln;
  }
  // check if the file is found in the directory given in the startup file
  if (pathName.CompareTo("???") == 0) { // not found in local directory search
    for (UInt_t i=0; i<fDataPath.size(); i++) {
      str = fDataPath[i] + TString("/") + fln;
      if (gSystem->AccessPathName(str.Data())!=true) { // found
        pathName = str;
        break;
      }
    }
  }
  // check if the file is found in the directories given by MUSRFULLDATAPATH
  const Char_t *musrpath = gSystem->Getenv("MUSRFULLDATAPATH");
  if (pathName.CompareTo("???") == 0) { // not found in local directory and xml path
    str = TString(musrpath);
    // MUSRFULLDATAPATH has the structure: path_1:path_2:...:path_n
    TObjArray *tokens = str.Tokenize(":");
    TObjString *ostr;
    for (Int_t i=0; i<tokens->GetEntries(); i++) {
      ostr = dynamic_cast<TObjString*>(tokens->At(i));
      str = ostr->GetString() + TString("/") + fln;
      if (gSystem->AccessPathName(str.Data())!=true) { // found
        pathName = str;
        break;
      }
    }
    // cleanup
    if (tokens) {
      delete tokens;
      tokens = 0;
    }
  }
  // no proper path name found
  if (pathName.CompareTo("???") == 0) {
    cerr << endl << ">> PRunDataHandler::FileExistsCheck(): **ERROR** Couldn't find '" << fln.Data() << "' in any standard path.";
    cerr << endl << ">>  standard search pathes are:";
    cerr << endl << ">>  1. the local directory";
    cerr << endl << ">>  2. the data directory given in the startup XML file";
    cerr << endl << ">>  3. the directories listed in MUSRFULLDATAPATH";
    return false;
  }

  fRunPathName = pathName;

  return true;
}

//--------------------------------------------------------------------------
// ReadRootFile
//--------------------------------------------------------------------------
/**
 * <p> Reads the LEM-data ROOT-files.
 *
 * <b>return:</b>
 * - true at successful reading,
 * - otherwise false.
 *
 * \param tag This tag is used as a switch between "Not Post Pileup Corrected",
 *            "Post Pileup Corrected", and "Read All" histogramms.
 */
Bool_t PRunDataHandler::ReadRootFile(UInt_t tag)
{
  PDoubleVector histoData;
  PRawRunData   runData;

  TFile f(fRunPathName.Data());
  if (f.IsZombie()) {
    return false;
  }

  TFolder *folder;
  f.GetObject("RunInfo", folder);
  if (!folder) {
    cerr << endl << ">> PRunDataHandler::ReadRootFile: **ERROR** Couldn't obtain RunInfo from " << fRunPathName.Data() << endl;
    f.Close();
    return false;
  }

  // read header and check if some missing run info need to be fed
  TLemRunHeader *runHeader = dynamic_cast<TLemRunHeader*>(folder->FindObjectAny("TLemRunHeader"));

  // check if run header is valid
  if (!runHeader) {
    cerr << endl << ">> PRunDataHandler::ReadRootFile: **ERROR** Couldn't obtain run header info from ROOT file " << fRunPathName.Data() << endl;
    f.Close();
    return false;
  }

  // get run title
  TObjString ostr = runHeader->GetRunTitle();
  runData.SetRunTitle(ostr.GetString());

  // get run number
  runData.SetRunNumber((Int_t)runHeader->GetRunNumber());

  // get temperature
  runData.ClearTemperature();
  runData.SetTemperature(0, runHeader->GetSampleTemperature(), runHeader->GetSampleTemperatureError());

  // get field
  runData.SetField(runHeader->GetSampleBField());

  // get implantation energy
  runData.SetEnergy(runHeader->GetImpEnergy());

  // get moderator HV
  runData.SetTransport(runHeader->GetModeratorHV());

  // get setup
  runData.SetSetup(runHeader->GetLemSetup().GetString());

  // get start time/date
  // start date
  time_t idt = (time_t)runHeader->GetStartTime();
  runData.SetStartDateTime(idt);
  struct tm *dt = localtime(&idt);
  TString stime("");
  Int_t yy = dt->tm_year;
  if (yy > 100)
    yy -= 100;
  if (yy < 10)
    stime += "0";
  stime += yy;
  stime += "-";
  stime += GetMonth(dt->tm_mon);
  stime += "-";
  if (dt->tm_mday < 10)
    stime += "0";
  stime += dt->tm_mday;
  runData.SetStartDate(stime);
  // start time
  if (dt->tm_hour == 0)
    stime = "00";
  else if (dt->tm_hour < 10)
    stime = "0";
  else
    stime = "";
  stime  += dt->tm_hour;
  stime += ":";
  if (dt->tm_min < 10)
    stime += "0";
  stime += dt->tm_min;
  stime += ":";
  if (dt->tm_sec < 10)
    stime += "0";
  stime += dt->tm_sec;
  runData.SetStartTime(stime);

  // get stop time/date
  // stop date
  idt = (time_t)runHeader->GetStopTime();
  runData.SetStopDateTime(idt);
  dt = localtime(&idt);
  stime = "";
  yy = dt->tm_year;
  if (yy > 100)
    yy -= 100;
  if (yy < 10)
    stime += "0";
  stime += yy;
  stime += "-";
  stime += GetMonth(dt->tm_mon);
  stime += "-";
  if (dt->tm_mday < 10)
    stime += "0";
  stime += dt->tm_mday;
  runData.SetStopDate(stime);
  // stop time
  if (dt->tm_hour == 0)
    stime = "00";
  else if (dt->tm_hour < 10)
    stime = "0";
  else
    stime = "";
  stime  += dt->tm_hour;
  stime += ":";
  if (dt->tm_min < 10)
    stime += "0";
  stime += dt->tm_min;
  stime += ":";
  if (dt->tm_sec < 10)
    stime += "0";
  stime += dt->tm_sec;
  runData.SetStopTime(stime);

  // get time resolution
  runData.SetTimeResolution(runHeader->GetTimeResolution());

  // get number of histogramms
  Int_t noOfHistos = runHeader->GetNHist();

  // get t0's
  Double_t *t0 = runHeader->GetTimeZero();
  // check if t0's are there
  if (t0[0] != -1) { // ugly, but at the moment there is no other way
    // copy t0's so they are not lost
    for (Int_t i=0; i<noOfHistos; i++) {
      runData.AppendT0((Int_t)t0[i]);
    }
  }

  // read run summary to obtain ring anode HV values
  TObjArray *runSummary = dynamic_cast<TObjArray*>(folder->FindObjectAny("RunSummary"));

  // check if run summary is valid
  if (!runSummary) {
    cout << endl << "**INFO** Couldn't obtain run summary info from ROOT file " << fRunPathName.Data() << endl;
    // this is not fatal... only RA-HV values are not available
  } else { // it follows a (at least) little bit strange extraction of the RA values from Thomas' TObjArray...
           //streaming of a ASCII-file would be more easy
    TString s, tok;
    TObjArrayIter summIter(runSummary);
    TObjString *os(dynamic_cast<TObjString*>(summIter.Next()));
    TObjArray *oa(0);
    TObjString *objTok(0);
    while (os != 0) {
      s = os->GetString();
      // will put four parallel if's since it may be that more than one RA-values are on one line
      if (s.Contains("RA-L")) {
        oa = s.Tokenize(" ");
        TObjArrayIter lineIter(oa);
        objTok = dynamic_cast<TObjString*>(lineIter.Next());
        while (objTok != 0) {
          if (!objTok->GetString().CompareTo("RA-L")) {
            objTok = dynamic_cast<TObjString*>(lineIter.Next()); // "="
            if (objTok != 0 && !objTok->GetString().CompareTo("=")) {
              objTok = dynamic_cast<TObjString*>(lineIter.Next()); // HV value
              runData.SetRingAnode(0, objTok->GetString().Atof()); // fill RA-R value into the runData structure
              break;
            }
          }
        objTok = dynamic_cast<TObjString*>(lineIter.Next()); // next token...
        }
      }

      if (s.Contains("RA-R")) {
        oa = s.Tokenize(" ");
        TObjArrayIter lineIter(oa);
        objTok = dynamic_cast<TObjString*>(lineIter.Next());
        while (objTok != 0){
          if (!objTok->GetString().CompareTo("RA-R")) {
            objTok = dynamic_cast<TObjString*>(lineIter.Next()); // "="
            if (objTok != 0 && !objTok->GetString().CompareTo("=")) {
              objTok = dynamic_cast<TObjString*>(lineIter.Next()); // HV value
              runData.SetRingAnode(1, objTok->GetString().Atof()); // fill RA-R value into the runData structure
              break;
            }
          }
        objTok = dynamic_cast<TObjString*>(lineIter.Next()); // next token...
        }
      }

      if (s.Contains("RA-T")) {
        oa = s.Tokenize(" ");
        TObjArrayIter lineIter(oa);
        objTok = dynamic_cast<TObjString*>(lineIter.Next());
        while (objTok != 0){
          if (!objTok->GetString().CompareTo("RA-T")) {
            objTok = dynamic_cast<TObjString*>(lineIter.Next()); // "="
            if (objTok != 0 && !objTok->GetString().CompareTo("=")) {
              objTok = dynamic_cast<TObjString*>(lineIter.Next()); // HV value
              runData.SetRingAnode(2, objTok->GetString().Atof()); // fill RA-T value into the runData structure
              break;
            }
          }
        objTok = dynamic_cast<TObjString*>(lineIter.Next()); // next token...
        }
      }

      if (s.Contains("RA-B")) {
        oa = s.Tokenize(" ");
        TObjArrayIter lineIter(oa);
        objTok = dynamic_cast<TObjString*>(lineIter.Next());
        while (objTok != 0){
          if (!objTok->GetString().CompareTo("RA-B")) {
            objTok = dynamic_cast<TObjString*>(lineIter.Next()); // "="
            if (objTok != 0 && !objTok->GetString().CompareTo("=")) {
              objTok = dynamic_cast<TObjString*>(lineIter.Next()); // HV value
              runData.SetRingAnode(3, objTok->GetString().Atof()); // fill RA-B value into the runData structure
              break;
            }
          }
        objTok = dynamic_cast<TObjString*>(lineIter.Next()); // next token...
        }
      }

      os = dynamic_cast<TObjString*>(summIter.Next()); // next summary line...
    }
  }

  // read data ---------------------------------------------------------
  // check if histos folder is found
  f.GetObject("histos", folder);
  if (!folder) {
    cerr << endl << ">> PRunDataHandler::ReadRootFile: **ERROR** Couldn't obtain histos from " << fRunPathName.Data() << endl;
    f.Close();
    return false;
  }
  // get all the data
  Char_t histoName[32];
  if (tag == ROOT_NPP) { // read the data which are NOT post pileup corrected
    for (Int_t i=0; i<noOfHistos; i++) {
      sprintf(histoName, "hDecay%02d", i);
      TH1F *histo = dynamic_cast<TH1F*>(folder->FindObjectAny(histoName));
      if (!histo) {
        cerr << endl << ">> PRunDataHandler::ReadRootFile: **ERROR** Couldn't get histo " << histoName;
        cerr << endl;
        return false;
      }
      // keep maximum of histogram as a T0 estimate
      runData.AppendT0Estimated(histo->GetMaximumBin());
      // fill data
      for (Int_t j=1; j<histo->GetNbinsX(); j++) {
        histoData.push_back(histo->GetBinContent(j));
      }
      // store them in runData vector
      runData.AppendDataBin(histoData);
      // clear histoData for the next histo
      histoData.clear();
    }
  } else if (tag == ROOT_PPC) { // read the data which ARE post pileup corrected
    for (Int_t i=0; i<noOfHistos; i++) {
      sprintf(histoName, "hDecay%02d", i+POST_PILEUP_HISTO_OFFSET);
      TH1F *histo = dynamic_cast<TH1F*>(folder->FindObjectAny(histoName));
      if (!histo) {
        cerr << endl << ">> PRunDataHandler::ReadRootFile: **ERROR** Couldn't get histo " << histoName;
        cerr << endl;
        return false;
      }
      // keep maximum of histogram as a T0 estimate
      runData.AppendT0Estimated(histo->GetMaximumBin());
      // fill data
      for (Int_t j=1; j<histo->GetNbinsX(); j++)
        histoData.push_back(histo->GetBinContent(j));
      // store them in runData vector
      runData.AppendDataBin(histoData);
      // clear histoData for the next histo
      histoData.clear();
    }
  } else { // read PPC and NPP histos
    for (Int_t i=0; i<noOfHistos; i++) {
      sprintf(histoName, "hDecay%02d", i);
      TH1F *histo = dynamic_cast<TH1F*>(folder->FindObjectAny(histoName));
      if (!histo) {
        cerr << endl << ">> PRunDataHandler::ReadRootFile: **ERROR** Couldn't get histo " << histoName;
        cerr << endl;
        return false;
      }
      // keep maximum of histogram as a T0 estimate
      runData.AppendT0Estimated(histo->GetMaximumBin());
      // fill data
      for (Int_t j=1; j<histo->GetNbinsX(); j++) {
        histoData.push_back(histo->GetBinContent(j));
      }
      // store them in runData vector
      runData.AppendDataBin(histoData);
      // clear histoData for the next histo
      histoData.clear();
    }
    for (Int_t i=0; i<noOfHistos; i++) {
      sprintf(histoName, "hDecay%02d", i+POST_PILEUP_HISTO_OFFSET);
      TH1F *histo = dynamic_cast<TH1F*>(folder->FindObjectAny(histoName));
      if (!histo) {
        cerr << endl << ">> PRunDataHandler::ReadRootFile: **ERROR** Couldn't get histo " << histoName;
        cerr << endl;
        return false;
      }
      // keep maximum of histogram as a T0 estimate
      runData.AppendT0Estimated(histo->GetMaximumBin());
      // fill data
      for (Int_t j=1; j<histo->GetNbinsX(); j++)
        histoData.push_back(histo->GetBinContent(j));
      // store them in runData vector
      runData.AppendDataBin(histoData);
      // clear histoData for the next histo
      histoData.clear();
    }
  }

  f.Close();

  // keep run name
  runData.SetRunName(fRunName);

  // add run to the run list
  fData.push_back(runData);

  return true;
}

//--------------------------------------------------------------------------
// ReadNexusFile
//--------------------------------------------------------------------------
/**
 * <p> Will read the NeXuS File Format as soon as PSI will have an implementation.
 *
 * <b>return:</b>
 * - true at successful reading,
 * - otherwise false.
 */
Bool_t PRunDataHandler::ReadNexusFile()
{
#ifdef PNEXUS_ENABLED
  cout << endl << ">> PRunDataHandler::ReadNexusFile(): Will read nexus file " << fRunPathName.Data() << " ...";

  PDoubleVector histoData;
  PRawRunData   runData;
  TString str;
  Double_t dval;

  PNeXus *nxs_file = new PNeXus(fRunPathName.Data());
  if (!nxs_file->IsValid()) {
    cerr << endl << ">> PRunDataHandler::ReadNexusFile(): Not a valid NeXus file.";
    cerr << endl << ">> Error Message: " << nxs_file->GetErrorMsg().data() << endl;
    return false;
  }

  // get header information

  // get/set run title
  str = TString(nxs_file->GetRunTitle());
  runData.SetRunTitle(str);

  // get/set run number
  runData.SetRunNumber(nxs_file->GetRunNumber());

  // get/set temperature
  runData.SetTemperature(0, nxs_file->GetSampleTemperature(), 0.0);

  // get/set field
  dval = nxs_file->GetMagneticField();
  str = TString(nxs_file->GetMagneticFieldUnits());
  // since field has to be given in Gauss, check the units
  Double_t factor=1.0;
  if (!str.CompareTo("gauss", TString::kIgnoreCase))
    factor=1.0;
  else if (!str.CompareTo("tesla", TString::kIgnoreCase))
    factor=1.0e4;
  else
    factor=1.0;
  runData.SetField(factor*dval);

  // get/set implantation energy
  runData.SetEnergy(PMUSR_UNDEFINED);

  // get/set moderator HV
  runData.SetTransport(PMUSR_UNDEFINED);

  // get/set RA HV's (LEM specific)
  for (UInt_t i=0; i<4; i++)
    runData.SetRingAnode(i, PMUSR_UNDEFINED);

  // get/set setup
  runData.SetSetup(nxs_file->GetRunNotes());

  // get/set sample
  runData.SetSample(nxs_file->GetSampleName());

  // get/set orientation
  runData.SetOrientation("??");

  // get/set time resolution (ns)
  runData.SetTimeResolution(nxs_file->GetTimeResolution());

  // get/set start/stop time
  runData.SetStartTime(nxs_file->GetStartTime());
  runData.SetStartDate(nxs_file->GetStartDate());
  runData.SetStopTime(nxs_file->GetStopTime());
  runData.SetStopDate(nxs_file->GetStopDate());

  // get t0, firstGoodBin, lastGoodBin, data
  UInt_t t0=nxs_file->GetT0();
  PIntPair goodDataBin;
  goodDataBin.first  = nxs_file->GetFirstGoodBin();
  goodDataBin.second = nxs_file->GetLastGoodBin();
  PDoubleVector data;
  for (Int_t i=0; i<nxs_file->GetNoHistos(); i++) {
    runData.AppendT0(t0);
    runData.AppendGoodDataBin(goodDataBin);
    data.clear();
    for (UInt_t j=0; j<nxs_file->GetHisto(i)->size(); j++) {
      data.push_back((Double_t)nxs_file->GetHisto(i)->at(j));
    }
    runData.AppendDataBin(data);
  }
  data.clear();

  // keep run name from the msr-file
  runData.SetRunName(fRunName);

  // keep the information
  fData.push_back(runData);

  // clean up
  if (nxs_file) {
    delete nxs_file;
    nxs_file = 0;
  }
#else
  cout << endl << ">> PRunDataHandler::ReadNexusFile(): Sorry, not enabled at configuration level, i.e. --enable-NeXus when executing configure" << endl << endl;
#endif
  return true;
}

//--------------------------------------------------------------------------
// ReadWkmFile
//--------------------------------------------------------------------------
/**
 * <p> Reads, for backwards compatibility, the ascii-wkm-file data format.
 * The routine is clever enough to distinguish the different wkm-flavours (argh).
 *
 * <b>return:</b>
 * - true at successful reading,
 * - otherwise false.
 */
Bool_t PRunDataHandler::ReadWkmFile()
{
  PDoubleVector histoData;
  PRawRunData runData;

  // open file
  ifstream f;

  // open wkm-file
  f.open(fRunPathName.Data(), ifstream::in);
  if (!f.is_open()) {
    cerr << endl << ">> PRunDataHandler::ReadWkmFile: **ERROR** Couldn't open run data (" << fRunPathName.Data() << ") file for reading, sorry ...";
    cerr << endl;
    return false;
  }

  // read header
  Bool_t    headerInfo = true;
  Char_t    instr[512];
  TString line, linecp;
  Double_t  dval;
  Int_t     ival;
  Bool_t    ok;
  Int_t     groups = 0, channels = 0;

  // skip leading empty lines
  do {
    f.getline(instr, sizeof(instr));
    line = TString(instr);
    if (!line.IsWhitespace())
      break;
  } while (!f.eof());

  // real header data should start here
  Ssiz_t idx;
  do {
    line = TString(instr);
    if (line.IsDigit()) { // end of header reached
      headerInfo = false;
    } else { // real stuff, hence filter data
      if (line.Contains("Title") || line.Contains("Titel")) {
        idx = line.Index(":");
        line.Replace(0, idx+1, 0, 0); // remove 'Title:'
        StripWhitespace(line);
        runData.SetRunTitle(line);
      } else if (line.Contains("Run:")) {
        idx = line.Index(":");
        line.Replace(0, idx+1, 0, 0); // remove 'Run:'
        StripWhitespace(line);
        ival = ToInt(line, ok);
        if (ok)
          runData.SetRunNumber(ival);
      } else if (line.Contains("Field")) {
        idx = line.Index(":");
        line.Replace(0, idx+1, 0, 0); // remove 'Field:'
        StripWhitespace(line);
        idx = line.Index("G"); // check if unit is given
        if (idx > 0) // unit is indeed given
          line.Resize(idx);
        dval = ToDouble(line, ok);
        if (ok)
          runData.SetField(dval);
      } else if (line.Contains("Setup")) {
        idx = line.Index(":");
        line.Replace(0, idx+1, 0, 0); // remove 'Setup:'
        StripWhitespace(line);
        runData.SetSetup(line);
      } else if (line.Contains("Temp:") || line.Contains("Temp(meas1):")) {
        linecp = line;
        idx = line.Index(":");
        line.Replace(0, idx+1, 0, 0); // remove 'Temp:'
        StripWhitespace(line);
        idx = line.Index("+/-"); // remove "+/- ..." part
        if (idx > 0)
          line.Resize(idx);
        idx = line.Index("K"); // remove "K ..." part
        if (idx > 0)
          line.Resize(idx);
        dval = ToDouble(line, ok);
        if (ok) {
          runData.SetTemperature(0, dval, 0.0);
        }
        idx = linecp.Index("+/-"); // get the error
        linecp.Replace(0, idx+3, 0, 0);
        StripWhitespace(linecp);
        dval = ToDouble(linecp, ok);
        if (ok) {
          runData.SetTempError(0, dval);
        }
      } else if (line.Contains("Temp(meas2):")) {
        linecp = line;
        idx = line.Index(":");
        line.Replace(0, idx+1, 0, 0); // remove 'Temp(meas2):'
        StripWhitespace(line);
        idx = line.Index("+/-"); // remove "+/- ..." part
        if (idx > 0)
          line.Resize(idx);
        idx = line.Index("K"); // remove "K ..." part
        if (idx > 0)
          line.Resize(idx);
        dval = ToDouble(line, ok);
        if (ok) {
          runData.SetTemperature(1, dval, 0.0);
        }
        idx = linecp.Index("+/-"); // get the error
        linecp.Replace(0, idx+3, 0, 0);
        StripWhitespace(linecp);
        dval = ToDouble(linecp, ok);
        if (ok) {
          runData.SetTempError(1, dval);
        }
      } else if (line.Contains("Groups")) {
        idx = line.Index(":");
        line.Replace(0, idx+1, 0, 0); // remove 'Groups:'
        StripWhitespace(line);
        ival = ToInt(line, ok);
        if (ok)
          groups = ival;
      } else if (line.Contains("Channels")) {
        idx = line.Index(":");
        line.Replace(0, idx+1, 0, 0); // remove 'Channels:'
        StripWhitespace(line);
        ival = ToInt(line, ok);
        if (ok)
          channels = ival;
      } else if (line.Contains("Resolution")) {
        idx = line.Index(":");
        line.Replace(0, idx+1, 0, 0); // remove 'Resolution:'
        StripWhitespace(line);
        dval = ToDouble(line, ok);
        if (ok)
          runData.SetTimeResolution(dval*1.0e3); // us -> ns
      }
    }

    if (headerInfo)
      f.getline(instr, sizeof(instr));
  } while (headerInfo && !f.eof());

  if ((groups == 0) || (channels == 0) || runData.GetTimeResolution() == 0.0) {
    cerr << endl << ">> PRunDataHandler::ReadWkmFile(): **ERROR** essential header informations are missing!";
    cerr << endl << ">> groups          = " << groups;
    cerr << endl << ">> channels        = " << channels;
    cerr << endl << ">> time resolution = " << runData.GetTimeResolution();
    cerr << endl;
    f.close();
    return false;
  }

  // read data ---------------------------------------------------------
  UInt_t group_counter = 0;
  Int_t val;
  TObjArray *tokens;
  TObjString *ostr;
  TString str;
  do {
    // check if empty line, i.e. new group
    if (IsWhitespace(instr)) {
      runData.AppendDataBin(histoData);
      // get a T0 estimate
      Double_t maxVal = 0.0;
      Int_t maxBin = 0;
      for (UInt_t i=0; i<histoData.size(); i++) {
        if (histoData[i] > maxVal) {
          maxVal = histoData[i];
          maxBin = i;
        }
      }
      runData.AppendT0Estimated(maxBin);
      histoData.clear();
      group_counter++;
    } else {
      // extract values
      line = TString(instr);
      // check if line starts with character. Needed for RAL WKM format
      if (!line.IsDigit()) {
        f.getline(instr, sizeof(instr));
        continue;
      }
      tokens = line.Tokenize(" ");

      if (!tokens) { // no tokens found
        cerr << endl << ">> PRunDataHandler::ReadWkmFile(): **ERROR** while reading data: coulnd't tokenize run data.";
        return false;
      }
      for (Int_t i=0; i<tokens->GetEntries(); i++) {
        ostr = dynamic_cast<TObjString*>(tokens->At(i));
        str = ostr->GetString();
        val = ToInt(str, ok);
        if (ok) {
          histoData.push_back(val);
        } else {
          cerr << endl << ">> PRunDataHandler::ReadWkmFile(): **ERROR** while reading data: data line contains non-integer values.";
          // clean up
          delete tokens;
          return false;
        }
      }
      // clean up
      if (tokens) {
        delete tokens;
        tokens = 0;
      }
    }

    f.getline(instr, sizeof(instr));

  } while (!f.eof());

  // handle last line if present
  if (strlen(instr) != 0) {
    // extract values
    line = TString(instr);
    tokens = line.Tokenize(" ");
    if (!tokens) { // no tokens found
      cerr << endl << ">> PRunDataHandler::ReadWkmFile(): **ERROR** while reading data: coulnd't tokenize run data.";
      return false;
    }
    for (Int_t i=0; i<tokens->GetEntries(); i++) {
      ostr = dynamic_cast<TObjString*>(tokens->At(i));
      str = ostr->GetString();
      val = ToInt(str, ok);
      if (ok) {
        histoData.push_back(val);
      } else {
        cerr << endl << ">> PRunDataHandler::ReadWkmFile(): **ERROR** while reading data: data line contains non-integer values.";
        // clean up
        delete tokens;
        return false;
      }
    }
    // clean up
    if (tokens) {
      delete tokens;
      tokens = 0;
    }
  }

  // save the last histo if not empty
  if (histoData.size() > 0) {
    runData.AppendDataBin(histoData);
    histoData.clear();
  }

  // close file
  f.close();

  // check if all groups are found
  if ((Int_t) runData.GetNoOfHistos() != groups) {
    cerr << endl << ">> PRunDataHandler::ReadWkmFile(): **ERROR**";
    cerr << endl << ">> expected " << groups << " histos, but found " << runData.GetNoOfHistos();
    return false;
  }

  // check if all groups have enough channels
  for (UInt_t i=0; i<runData.GetNoOfHistos(); i++) {
    if ((Int_t) runData.GetDataBin(i)->size() != channels) {
      cerr << endl << ">> PRunDataHandler::ReadWkmFile(): **ERROR**";
      cerr << endl << ">> expected " << channels << " bins in histo " << i << ", but found " << runData.GetDataBin(i)->size();
      return false;
    }
  }

  // keep run name
  runData.SetRunName(fRunName);

  // add run to the run list
  fData.push_back(runData);

  return true;
}

//--------------------------------------------------------------------------
// ReadPsiBinFile
//--------------------------------------------------------------------------
/**
 * <p> Reads the old-fashioned PSI-BIN data-files. The MuSR_td_PSI_bin class
 * of Alex Amato is used. In case of problems, please contact alex.amato@psi.ch.
 *
 * <b>return:</b>
 * - true at successful reading,
 * - otherwise false.
 */
Bool_t PRunDataHandler::ReadPsiBinFile()
{
  MuSR_td_PSI_bin psiBin;
  Int_t status;
  Bool_t success;

  // read psi bin file
  status = psiBin.read(fRunPathName.Data());
  switch (status) {
    case 0: // everything perfect
      success = true;
      break;
    case 1: // couldn't open file, or failed while reading the header
      cerr << endl << ">> **ERROR** couldn't open psi-bin file, or failed while reading the header";
      cerr << endl;
      success = false;
      break;
    case 2: // unsupported version of the data
      cerr << endl << ">> **ERROR** psi-bin file: unsupported version of the data";
      cerr << endl;
      success = false;
      break;
    case 3: // error when allocating data buffer
      cerr << endl << ">> **ERROR** psi-bin file: error when allocating data buffer";
      cerr << endl;
      success = false;
      break;
    case 4: // number of histograms/record not equals 1
      cerr << endl << ">> **ERROR** psi-bin file: number of histograms/record not equals 1";
      cerr << endl;
      success = false;
      break;
    default: // you never should have reached this point
      success = false;
      break;
  }

  // if any reading error happend, get out of here
  if (!success)
    return success;

  // fill necessary header informations
  PIntVector ivec;
  PRawRunData runData;
  Double_t dval;
  // keep run name
  runData.SetRunName(fRunName);
  // get run title
  runData.SetRunTitle(TString(psiBin.get_comment().c_str())); // run title
  // get run number
  runData.SetRunNumber(psiBin.get_runNumber_int());
  // get setup
  runData.SetSetup(TString(psiBin.get_comment().c_str()));
  // get sample
  runData.SetSample(TString(psiBin.get_sample().c_str()));
  // get orientation
  runData.SetOrientation(TString(psiBin.get_orient().c_str()));
  // set LEM specific information to default value since it is not in the file and not used...
  runData.SetEnergy(PMUSR_UNDEFINED);
  runData.SetTransport(PMUSR_UNDEFINED);
  // get field
  Double_t scale = 0.0;
  if (psiBin.get_field().rfind("G") != string::npos)
    scale = 1.0;
  if (psiBin.get_field().rfind("T") != string::npos)
    scale = 1.0e4;
  status = sscanf(psiBin.get_field().c_str(), "%lf", &dval);
  if (status == 1)
    runData.SetField(scale*dval);
  // get temperature
  PDoubleVector tempVec(psiBin.get_temperatures_vector());
  PDoubleVector tempDevVec(psiBin.get_devTemperatures_vector());
  if ((tempVec.size() > 1) && (tempDevVec.size() > 1) && tempVec[0] && tempVec[1]) {
    // take only the first two values for now...
    //maybe that's not enough - e.g. in older GPD data I saw the "correct values in the second and third entry..."
    for (UInt_t i(0); i<2; i++) {
      runData.SetTemperature(i, tempVec[i], tempDevVec[i]);
    }
    tempVec.clear();
    tempDevVec.clear();
  } else {
    status = sscanf(psiBin.get_temp().c_str(), "%lfK", &dval);
    if (status == 1)
      runData.SetTemperature(0, dval, 0.0);
  }

  // get time resolution (ns)
  runData.SetTimeResolution(psiBin.get_binWidth_ns());
  // get t0's
  ivec = psiBin.get_t0_vector();
  if (ivec.empty()) {
    cerr << endl << ">> **ERROR** psi-bin file: couldn't obtain any t0's";
    cerr << endl;
    return false;
  }
  for (UInt_t i=0; i<ivec.size(); i++)
    runData.AppendT0(ivec[i]);

  // get start/stop time
  vector<string> sDateTime = psiBin.get_timeStart_vector();
  if (sDateTime.size() < 2) {
    cerr << endl << ">> **WARNING** psi-bin file: couldn't obtain run start date/time" << endl;
  }
  runData.SetStartDate(sDateTime[0]);
  runData.SetStartTime(sDateTime[1]);
  sDateTime.clear();

  sDateTime = psiBin.get_timeStop_vector();
  if (sDateTime.size() < 2) {
    cerr << endl << ">> **WARNING** psi-bin file: couldn't obtain run stop date/time" << endl;
  }
  runData.SetStopDate(sDateTime[0]);
  runData.SetStopTime(sDateTime[1]);
  sDateTime.clear();

  // fill raw data
  PDoubleVector histoData;
  Int_t *histo;
  for (Int_t i=0; i<psiBin.get_numberHisto_int(); i++) {
    histo = psiBin.get_histo_array_int(i);
    for (Int_t j=0; j<psiBin.get_histoLength_bin(); j++) {
      histoData.push_back(histo[j]);
    }
    delete[] histo;
    runData.AppendDataBin(histoData);
    // estimate T0 from maximum of the data
    Double_t maxVal = 0.0;
    Int_t maxBin = 0;
    for (UInt_t j=0; j<histoData.size(); j++) {
      if (histoData[j] > maxVal) {
        maxVal = histoData[j];
        maxBin = j;
      }
    }
    runData.AppendT0Estimated(maxBin);
    histoData.clear();
  }

  // add run to the run list
  fData.push_back(runData);

  return success;
}

//--------------------------------------------------------------------------
// ReadMudFile
//--------------------------------------------------------------------------
/**
 * <p> Reads the triumf mud-file format.
 *
 * <b>return:</b>
 * - true at successful reading,
 * - otherwise false.
 */
Bool_t PRunDataHandler::ReadMudFile()
{
  Int_t    fh;
  UINT32   type, val;
  Int_t    success;
  Char_t   str[1024];
  Double_t dval;

  PRawRunData runData;

  fh = MUD_openRead((Char_t *)fRunPathName.Data(), &type);
  if (fh == -1) {
    cerr << endl << ">> **ERROR** Couldn't open mud-file " << fRunPathName.Data() << ", sorry.";
    cerr << endl;
    return false;
  }

  // read necessary header information

  // keep run name
  runData.SetRunName(fRunName);

  // get run title
  success = MUD_getTitle( fh, str, sizeof(str) );
  if ( !success ) {
    cerr << endl << ">> **WARNING** Couldn't obtain the run title of run " << fRunName.Data();
    cerr << endl;
  }
  runData.SetRunTitle(TString(str));

  // get run number
  success = MUD_getRunNumber( fh, &val );
  if (success) {
    runData.SetRunNumber((Int_t)val);
  }

  // get start/stop time of the run
  time_t tval;
  struct tm *dt;
  TString stime("");
  Int_t yy = 0;
  success = MUD_getTimeBegin( fh, (UINT32*)&tval );
  if (success) {
    runData.SetStartDateTime((const time_t)tval);
    dt = localtime((const time_t*)&tval);
    assert(dt);
    yy = dt->tm_year;
    if (yy > 100)
      yy -= 100;
    if (yy < 10)
      stime += "0";
    stime += yy;
    stime += "-";
    stime += GetMonth(dt->tm_mon);
    stime += "-";
    if (dt->tm_mday < 10)
      stime += "0";
    stime += dt->tm_mday;
    runData.SetStartDate(stime);
    // start time
    if (dt->tm_hour == 0)
      stime = "00";
    else if (dt->tm_hour < 10)
      stime = "0";
    else
      stime = "";
    stime  += dt->tm_hour;
    stime += ":";
    if (dt->tm_min < 10)
      stime += "0";
    stime += dt->tm_min;
    stime += ":";
    if (dt->tm_sec < 10)
      stime += "0";
    stime += dt->tm_sec;
    runData.SetStartTime(stime);
  }

  stime = TString("");
  success = MUD_getTimeEnd( fh, (UINT32*)&tval );
  if (success) {
    runData.SetStopDateTime((const time_t)tval);
    dt = localtime((const time_t*)&tval);
    assert(dt);
    stime = "";
    yy = dt->tm_year;
    if (yy > 100)
      yy -= 100;
    if (yy < 10)
      stime += "0";
    stime += yy;
    stime += "-";
    stime += GetMonth(dt->tm_mon);
    stime += "-";
    if (dt->tm_mday < 10)
      stime += "0";
    stime += dt->tm_mday;
    runData.SetStopDate(stime);
    // stop time
    if (dt->tm_hour == 0)
      stime = "00";
    else if (dt->tm_hour < 10)
      stime = "0";
    else
      stime = "";
    stime  += dt->tm_hour;
    stime += ":";
    if (dt->tm_min < 10)
      stime += "0";
    stime += dt->tm_min;
    stime += ":";
    if (dt->tm_sec < 10)
      stime += "0";
    stime += dt->tm_sec;
    runData.SetStopTime(stime);
  }

  // get setup
  TString setup;
  success = MUD_getLab( fh, str, sizeof(str) );
  if (success) {
    setup = TString(str) + TString("/");
  }
  success = MUD_getArea( fh, str, sizeof(str) );
  if (success) {
    setup += TString(str) + TString("/");
  }
  success = MUD_getApparatus( fh, str, sizeof(str) );
  if (success) {
    setup += TString(str) + TString("/");
  }
  success = MUD_getSample( fh, str, sizeof(str) );
  if (success) {
    setup += TString(str);
    runData.SetSample(str);
  }
  runData.SetSetup(setup);

  // set LEM specific information to default value since it is not in the file and not used...
  runData.SetEnergy(PMUSR_UNDEFINED);
  runData.SetTransport(PMUSR_UNDEFINED);

  // get field
  success = MUD_getField( fh, str, sizeof(str) );
  if (success) {
    success = sscanf(str, "%lf G", &dval);
    if (success == 1) {
      runData.SetField(dval);
    } else {
      runData.SetField(PMUSR_UNDEFINED);
    }
  } else {
    runData.SetField(PMUSR_UNDEFINED);
  }

  // get temperature
  success = MUD_getTemperature( fh, str, sizeof(str) );
  if (success) {
    success = sscanf(str, "%lf K", &dval);
    if (success == 1) {
      runData.SetTemperature(0, dval, 0.0);
    } else {
      runData.SetTemperature(0, PMUSR_UNDEFINED, 0.0);
    }
  } else {
    runData.SetTemperature(0, PMUSR_UNDEFINED, 0.0);
  }

  // get number of histogramms
  success = MUD_getHists(fh, &type, &val);
  if ( !success ) {
    cerr << endl << ">> **ERROR** Couldn't obtain the number of histograms of run " << fRunName.Data();
    cerr << endl;
    MUD_closeRead(fh);
    return false;
  }
  Int_t noOfHistos = (Int_t)val;

  // get time resolution (ns)
  // check that time resolution is identical for all histograms
  // >> currently it is not forseen to handle histos with different time resolutions <<
  // >> perhaps this needs to be reconsidered later on                               <<
  REAL64 timeResolution = 0.0; // in seconds!!
  REAL64 lrval = 0.0;
  for (Int_t i=1; i<=noOfHistos; i++) {
    success = MUD_getHistSecondsPerBin( fh, i, &lrval );
    if (!success) {
      cerr << endl << ">> **ERROR** Couldn't obtain the time resolution of run " << fRunName.Data();
      cerr << endl << ">>           which is fatal, sorry.";
      cerr << endl;
      MUD_closeRead(fh);
      return false;
    }
    if (i==1) {
      timeResolution = lrval;
    } else {
      if (lrval != timeResolution) {
        cerr << endl << ">> **ERROR** various time resolutions found in run " << fRunName.Data();
        cerr << endl << ">>           this is currently not supported, sorry.";
        cerr << endl;
        MUD_closeRead(fh);
        return false;
      }
    }
  }

  runData.SetTimeResolution((Double_t)timeResolution * 1.0e9); // s -> ns

  // read histograms
  pair<Int_t, Int_t> valPair;
  UINT32 *pData; // histo memory
  pData = NULL;
  PDoubleVector histoData;
  UInt_t noOfBins;

  for (Int_t i=1; i<=noOfHistos; i++) {

    // get t0's
    success = MUD_getHistT0_Bin( fh, i, &val );
    if ( !success ) {
      cerr << endl << ">> **WARNING** Couldn't get t0 of histo " << i << " of run " << fRunName.Data();
      cerr << endl;
    }
    runData.AppendT0((Int_t)val);

    // get bkg bins
    success = MUD_getHistBkgd1( fh, i, &val );
    if ( !success ) {
      cerr << endl << ">> **WARNING** Couldn't get bkg bin 1 of histo " << i << " of run " << fRunName.Data();
      cerr << endl;
      valPair.first = -1;
    } else {
      valPair.first = (Int_t)val;
    }

    success = MUD_getHistBkgd2( fh, i, &val );
    if ( !success ) {
      cerr << endl << ">> **WARNING** Couldn't get bkg bin 2 of histo " << i << " of run " << fRunName.Data();
      cerr << endl;
      valPair.second = -1;
    }
    valPair.second = (Int_t)val;

    if ((valPair.first != -1) && (valPair.second != -1)) { // bkg bin1 && bkg bin2 found
      runData.AppendBkgBin(valPair);
    }

    // get good data bins
    success = MUD_getHistGoodBin1( fh, i, &val );
    if ( !success ) {
      cerr << endl << ">> **WARNING** Couldn't get good bin 1 of histo " << i << " of run " << fRunName.Data();
      cerr << endl;
      valPair.first = -1;
    } else {
      valPair.first = (Int_t)val;
    }

    success = MUD_getHistGoodBin2( fh, i, &val );
    if ( !success ) {
      cerr << endl << ">> **WARNING** Couldn't get good bin 2 of histo " << i << " of run " << fRunName.Data();
      cerr << endl;
      valPair.second = -1;
    }
    valPair.second = (Int_t)val;

    if ((valPair.first != -1) && (valPair.second != -1)) { // good bin1 && good bin2 found
      runData.AppendGoodDataBin(valPair);
    }

    // get number of bins
    success = MUD_getHistNumBins( fh, i, &val );
    if ( !success ) {
      cerr << endl << ">> **ERROR** Couldn't get the number of bins of histo " << i << ".";
      cerr << endl << ">>           This is fatal, sorry.";
      cerr << endl;
      MUD_closeRead( fh );
      return false;
    }
    noOfBins = (UInt_t)val;

    pData = (UINT32*)malloc(noOfBins*sizeof(pData));
    if (pData == NULL) {
      cerr << endl << ">> **ERROR** Couldn't allocate memory for data.";
      cerr << endl << ">>           This is fatal, sorry.";
      cerr << endl;
      MUD_closeRead( fh );
      return false;
    }

    // get histogram
    success = MUD_getHistData( fh, i, pData );
    if ( !success ) {
      cerr << endl << ">> **ERROR** Couldn't get histo no " << i << ".";
      cerr << endl << ">>           This is fatal, sorry.";
      cerr << endl;
      MUD_closeRead( fh );
      return false;
    }

    for (UInt_t j=0; j<noOfBins; j++) {
      histoData.push_back(pData[j]);
    }
    runData.AppendDataBin(histoData);

    // estimate T0 from maximum of the data
    Double_t maxVal = 0.0;
    Int_t maxBin = 0;
    for (UInt_t j=0; j<histoData.size(); j++) {
      if (histoData[j] > maxVal) {
        maxVal = histoData[j];
        maxBin = j;
      }
    }
    runData.AppendT0Estimated(maxBin);
    histoData.clear();

    free(pData);
  }

  MUD_closeRead(fh);

  // add run to the run list
  fData.push_back(runData);

  return true;
}

//--------------------------------------------------------------------------
// ReadMduAsciiFile
//--------------------------------------------------------------------------
/**
 * <p>Reads the mdu ascii files (PSI). Needed to work around PSI-BIN limitations.
 *
 * <p>Lines starting with '#' or '%' are considered as comment lines. The file has 
 * the following structure:
 * \verbatim
 * HEADER
 * TITLE: title-string
 * SETUP: setup-string
 * FIELD: val-string (G) or (T) (e.g. 123456 (G))
 * TEMP: val-string (K)
 * GROUPS: # of histograms written
 * CHANNELS: # of bins per histogram written
 * RESOLUTION: timeresolution (fs) or (ps) or (ns) or (us)
 * \endverbatim
 * followed by the data, which are written in column format, starting with the DATA
 * tag, i.e.
 * \verbatim
 * DATA
 * 
 * \endverbatim
 *
 * <b>return:</b>
 * - true at successful reading,
 * - otherwise false.
 */
Bool_t PRunDataHandler::ReadMduAsciiFile()
{
  Bool_t success = true;
  
  // open file
  ifstream f;

  // open data-file
  f.open(fRunPathName.Data(), ifstream::in);
  if (!f.is_open()) {
    cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** Couldn't open data file (" << fRunPathName.Data() << ") for reading, sorry ...";
    cerr << endl;
    return false;
  }

  PRawRunData runData;
  
  // keep run name
  runData.SetRunName(fRunName);
  
  Int_t     lineNo = 0;
  Char_t    instr[512];
  TString line, workStr;
  Bool_t    headerTag = false;
  Bool_t    dataTag = false;
  Int_t     dataLineCounter = 0;
  TObjString *ostr;
  TObjArray *tokens = 0;
  TString str;
  Int_t groups = 0;
  Int_t channels = 0;
  Double_t dval = 0.0, unitScaling = 0.0;
  vector<PDoubleVector> data;
  
  while (!f.eof()) {
    f.getline(instr, sizeof(instr));
    line = TString(instr);
    lineNo++;

    // ignore comment lines
    if (line.BeginsWith("#") || line.BeginsWith("%"))
      continue;

    // ignore empty lines
    if (line.IsWhitespace())
      continue;
        
    // check if header tag
    workStr = line;
    workStr.Remove(TString::kLeading, ' '); // remove spaces from the begining
    if (workStr.BeginsWith("header", TString::kIgnoreCase)) {
      headerTag = true;
      dataTag = false;
      continue;
    }

    // check if data tag
    workStr = line;
    workStr.Remove(TString::kLeading, ' '); // remove spaces from the beining
    if (workStr.BeginsWith("data", TString::kIgnoreCase)) {
      headerTag = false;
      dataTag = true;
      continue;
    }
    
    if (headerTag) {
      workStr = line;
      workStr.Remove(TString::kLeading, ' '); // remove spaces from the beining
      if (workStr.BeginsWith("title:", TString::kIgnoreCase)) {
        runData.SetRunTitle(TString(workStr.Data()+workStr.First(":")+2));
      } else if (workStr.BeginsWith("field:", TString::kIgnoreCase)) {
        tokens = workStr.Tokenize(":("); // field: val (units)
        // check if expected number of tokens present
        if (tokens->GetEntries() != 3) {
          cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", invalid field entry in header.";
          cerr << endl << ">> " << line.Data();
          cerr << endl;
          success = false;
          break;
        }
        // check if field value is a number
        ostr = dynamic_cast<TObjString*>(tokens->At(1));
        if (ostr->GetString().IsFloat()) {
          dval = ostr->GetString().Atof();
        } else {
          cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", field value is not float/doulbe.";
          cerr << endl << ">> " << line.Data();
          cerr << endl;
          success = false;
          break;
        }
        // check units, accept (G), (T)
        ostr = dynamic_cast<TObjString*>(tokens->At(2));
        if (ostr->GetString().Contains("G"))
          unitScaling = 1.0;
        else if (ostr->GetString().Contains("T"))
          unitScaling = 1.0e4;
        else {
          cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", unkown field units.";
          cerr << endl << ">> " << line.Data();
          cerr << endl;
          success = false;
          break;
        }
        runData.SetField(dval*unitScaling);  
        
        // clean up tokens
        if (tokens) {
          delete tokens;
          tokens = 0;
        }
      } else if (workStr.BeginsWith("temp:", TString::kIgnoreCase)) {
        tokens = workStr.Tokenize(":("); // temp: val (units)
        // check if expected number of tokens present
        if (tokens->GetEntries() != 3) {
          cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", invalid temperatue entry in header.";
          cerr << endl << ">> " << line.Data();
          cerr << endl;
          success = false;
          break;
        }
        // check if field value is a number
        ostr = dynamic_cast<TObjString*>(tokens->At(1));
        if (ostr->GetString().IsFloat()) {
          dval = ostr->GetString().Atof();
        } else {
          cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", temperature value is not float/doulbe.";
          cerr << endl << ">> " << line.Data();
          cerr << endl;
          success = false;
          break;
        }
        runData.SetTemperature(0, dval, 0.0);  
        
        // clean up tokens
        if (tokens) {
          delete tokens;
          tokens = 0;
        }
      } else if (workStr.BeginsWith("setup:", TString::kIgnoreCase)) {
        runData.SetSetup(TString(workStr.Data()+workStr.First(":")+2));
      } else if (workStr.BeginsWith("groups:", TString::kIgnoreCase)) {
        workStr = TString(workStr.Data()+workStr.First(":")+2);
        groups = workStr.Atoi();      
        if (groups == 0) {
          cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", groups is not a number or 0.";
          cerr << endl;
          success = false;
          break;
        }
        data.resize(groups);
      } else if (workStr.BeginsWith("channels:", TString::kIgnoreCase)) {
        workStr = TString(workStr.Data()+workStr.First(":")+2);
        channels = workStr.Atoi();
        if (channels == 0) {
          cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", channels is not a number or 0.";
          cerr << endl;
          success = false;
          break;
        }
      } else if (workStr.BeginsWith("resolution:", TString::kIgnoreCase)) {
        tokens = workStr.Tokenize(":("); // resolution: val (units)
        // check if expected number of tokens present
        if (tokens->GetEntries() != 3) {
          cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", invalid time resolution entry in header.";
          cerr << endl << line.Data();
          cerr << endl;
          success = false;
          break;
        }
        // check if timeresolution value is a number
        ostr = dynamic_cast<TObjString*>(tokens->At(1));
        if (ostr->GetString().IsFloat()) {
          dval = ostr->GetString().Atof();
        } else {
          cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", time resolution value is not float/doulbe.";
          cerr << endl << ">> " << line.Data();
          cerr << endl;
          success = false;
          break;
        }
        // check units, accept (fs), (ps), (ns), (us)
        ostr = dynamic_cast<TObjString*>(tokens->At(2));
        if (ostr->GetString().Contains("fs"))
          unitScaling = 1.0e-6;
        else if (ostr->GetString().Contains("ps"))
          unitScaling = 1.0e-3;
        else if (ostr->GetString().Contains("ns"))
          unitScaling = 1.0;
        else if (ostr->GetString().Contains("us"))
          unitScaling = 1.0e3;
        else {
          cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", unkown time resolution units.";
          cerr << endl << ">> " << line.Data();
          cerr << endl;
          success = false;
          break;
        }
        runData.SetTimeResolution(dval*unitScaling);  
        
        // clean up tokens
        if (tokens) {
          delete tokens;
          tokens = 0;
        }
      } else { // error
        cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** line no " << lineNo << ", illegal header line.";
        cerr << endl;
        success = false;
        break;
      }
    } else if (dataTag) {
      dataLineCounter++;
      tokens = line.Tokenize(" ,\t");
      // check if the number of data line entries is correct
      if (tokens->GetEntries() != groups+1) {
        cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **ERROR** found data line with a wrong data format, cannot be handled (line no " << lineNo << ")";
        cerr << endl << ">> line:";
        cerr << endl << ">> " << line.Data();
        cerr << endl;
        success = false;
        break;
      }
      
      for (Int_t i=1; i<tokens->GetEntries(); i++) {
        ostr = dynamic_cast<TObjString*>(tokens->At(i));
        data[i-1].push_back(ostr->GetString().Atof());
      }
      
      // clean up tokens
      if (tokens) {
        delete tokens;
        tokens = 0;
      }
    }
  }
  
  f.close();

  // keep data
  for (UInt_t i=0; i<data.size(); i++) {
    runData.AppendDataBin(data[i]);
  }
  
  // estimate T0 from maximum of the data
  for (UInt_t i=0; i<data.size(); i++) {
    Double_t maxVal = 0.0;
    Int_t maxBin = 0;
    for (UInt_t j=0; j<data[i].size(); j++) {
      if (data[i][j] > maxVal) {
        maxVal = data[i][j];
        maxBin = j;
      }
    }
    runData.AppendT0Estimated(maxBin);
  }

  // clean up
  for (UInt_t i=0; i<data.size(); i++) 
    data[i].clear();
  data.clear();  
  
  if (dataLineCounter != channels) {
    cerr << endl << ">> PRunDataHandler::ReadMduAsciiFile **WARNING** found " << dataLineCounter << " data bins,";
    cerr << endl << ">> expected " << channels << " according to the header." << endl;
  }

  fData.push_back(runData);
  
  return success;
}
 
//--------------------------------------------------------------------------
// ReadAsciiFile
//--------------------------------------------------------------------------
/**
 * <p>Reads ascii files. Intended for the nonMuSR data.
 *
 * The file format definition is:
 * Comment lines start with a '#' or '%' character.
 * The file can start with some header info. The header is optional, as all its tags, but 
 * if present it has the following format:
 *
 * \verbatim
 * HEADER
 * TITLE:  title
 * X-AXIS-TITLE: x-axis title
 * Y-AXIS-TITLE: y-axis title
 * SETUP:  setup
 * FIELD:  field
 * TEMP:   temperature
 * ENERGY: energy
 * \endverbatim
 *
 * field is assumed to be given in (G), the temperature in (K), the energy in (keV)
 *
 * The data are read column like and start with the data tag DATA, followed by the
 * data columns, i.e.:
 *
 * \verbatim
 * DATA
 * x, y [, error y]
 * \endverbatim
 *
 * where spaces, column, are a tab are possible separations.
 * If no error in y is present, the weighting in the fit will be equal.
 *
 * <b>return:</b>
 * - true at successful reading,
 * - otherwise false.
 */
Bool_t PRunDataHandler::ReadAsciiFile()
{
  Bool_t success = true;

  // open file
  ifstream f;

  // open data-file
  f.open(fRunPathName.Data(), ifstream::in);
  if (!f.is_open()) {
    cerr << endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** Couldn't open data file (" << fRunPathName.Data() << ") for reading, sorry ...";
    cerr << endl;
    return false;
  }

  PRawRunData runData;

  // init some stuff
  runData.fDataNonMusr.SetFromAscii(true);
  runData.fDataNonMusr.AppendLabel("??"); // x default label
  runData.fDataNonMusr.AppendLabel("??"); // y default label

  runData.SetRunName(fRunName); // keep the run name

  Int_t     lineNo = 0;
  Char_t    instr[512];
  TString line, workStr;
  Bool_t    headerTag = false;
  Bool_t    dataTag = false;
  Double_t  x, y, ey;
  PDoubleVector xVec, exVec, yVec, eyVec;

  while (!f.eof()) {
    f.getline(instr, sizeof(instr));
    line = TString(instr);
    lineNo++;

    // check if comment line
    if (line.BeginsWith("#") || line.BeginsWith("%"))
      continue;

    // check if header tag
    workStr = line;
    workStr.Remove(TString::kLeading, ' '); // remove spaces from the begining
    if (workStr.BeginsWith("header", TString::kIgnoreCase)) {
      headerTag = true;
      dataTag = false;
      continue;
    }

    // check if data tag
    workStr = line;
    workStr.Remove(TString::kLeading, ' '); // remove spaces from the beining
    if (workStr.BeginsWith("data", TString::kIgnoreCase)) {
      headerTag = false;
      dataTag = true;
      continue;
    }

    if (headerTag) {
      if (line.IsWhitespace()) // ignore empty lines
        continue;
      workStr = line;
      workStr.Remove(TString::kLeading, ' '); // remove spaces from the beining
      if (workStr.BeginsWith("title:", TString::kIgnoreCase)) {
        runData.SetRunTitle(TString(workStr.Data()+workStr.First(":")+2));
      } else if (workStr.BeginsWith("setup:", TString::kIgnoreCase)) {
        runData.SetSetup(TString(workStr.Data()+workStr.First(":")+2));
      } else if (workStr.BeginsWith("field:", TString::kIgnoreCase)) {
        workStr = TString(workStr.Data()+workStr.First(":")+2);
        if (!workStr.IsFloat()) {
          cerr << endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ", field is not a number.";
          cerr << endl;
          success = false;
          break;
        }
        runData.SetField(workStr.Atof());
      } else if (workStr.BeginsWith("x-axis-title:", TString::kIgnoreCase)) {
        runData.fDataNonMusr.SetLabel(0, TString(workStr.Data()+workStr.First(":")+2));
      } else if (workStr.BeginsWith("y-axis-title:", TString::kIgnoreCase)) {
        runData.fDataNonMusr.SetLabel(1, TString(workStr.Data()+workStr.First(":")+2));
      } else if (workStr.BeginsWith("temp:", TString::kIgnoreCase)) {
        workStr = TString(workStr.Data()+workStr.First(":")+2);
        if (!workStr.IsFloat()) {
          cerr << endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ", temperature is not a number.";
          cerr << endl;
          success = false;
          break;
        }
        runData.SetTemperature(0, workStr.Atof(), 0.0);
      } else if (workStr.BeginsWith("energy:", TString::kIgnoreCase)) {
        workStr = TString(workStr.Data()+workStr.First(":")+2);
        if (!workStr.IsFloat()) {
          cerr << endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ", energy is not a number.";
          cerr << endl;
          success = false;
          break;
        }
        runData.SetEnergy(workStr.Atof());
        runData.SetTransport(PMUSR_UNDEFINED); // just to initialize the variables to some "meaningful" value
      } else { // error
        cerr << endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ", illegal header line.";
        cerr << endl;
        success = false;
        break;
      }
    } else if (dataTag) {
      if (line.IsWhitespace()) // ignore empty lines
        continue;
      TObjString *ostr;
      TObjArray *tokens;

      // Remove trailing end of line
      line.Remove(TString::kTrailing, '\r'); 

      // check if data have x, y [, error y] structure, and that x, y, and error y are numbers
      tokens = line.Tokenize(" ,\t");
      // check if the number of data line entries is 2 or 3
      if ((tokens->GetEntries() != 2) && (tokens->GetEntries() != 3)) {
        cerr << endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** found data line with a structure different than \"x, y [, error y]\", cannot be handled (line no " << lineNo << ")";
        cerr << endl;
        success = false;
        break;
      }

      // get x
      ostr = dynamic_cast<TObjString*>(tokens->At(0));
      if (!ostr->GetString().IsFloat()) {
        cerr << endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ": x = " << ostr->GetString().Data() << " is not a number, sorry.";
        cerr << endl;
        success = false;
        break;
      }
      x = ostr->GetString().Atof();

      // get y
      ostr = dynamic_cast<TObjString*>(tokens->At(1));
      if (!ostr->GetString().IsFloat()) {
        cerr << endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ": y = " << ostr->GetString().Data() << " is not a number, sorry.";
        cerr << endl;
        success = false;
        break;
      }
      y = ostr->GetString().Atof();

      // get error y if present
      if (tokens->GetEntries() == 3) {
        ostr = dynamic_cast<TObjString*>(tokens->At(2));
        if (!ostr->GetString().IsFloat()) {
          cerr << endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << ": error y = " << ostr->GetString().Data() << " is not a number, sorry.";
          cerr << endl;
          success = false;
          break;
        }
        ey = ostr->GetString().Atof();
      } else {
        ey = 1.0;
      }

      // clean up tokens
      if (tokens) {
        delete tokens;
        tokens = 0;
      }

      // keep values
      xVec.push_back(x);
      exVec.push_back(1.0);
      yVec.push_back(y);
      eyVec.push_back(ey);

    } else {
      cerr << endl << ">> PRunDataHandler::ReadAsciiFile **ERROR** line no " << lineNo << " neither header nor data line. No idea how to handle it!";
      cerr << endl;
      success = false;
      break;
    }
  }

  f.close();

  // keep values
  runData.fDataNonMusr.AppendData(xVec);
  runData.fDataNonMusr.AppendErrData(exVec);
  runData.fDataNonMusr.AppendData(yVec);
  runData.fDataNonMusr.AppendErrData(eyVec);

  fData.push_back(runData);

  // clean up
  xVec.clear();
  exVec.clear();
  yVec.clear();
  eyVec.clear();

  return success;
}

//--------------------------------------------------------------------------
// ReadDBFile
//--------------------------------------------------------------------------
/**
 * <p>Reads triumf db-files. Intended for the nonMuSR data.
 *
 * <p>The file format definition is:
 * The following is a description of the features of the TRIUMF .db file format that are 
 * currently recognized by musrfit/musrview. The available commands include: title, abstract, 
 * comments, labels, and data.
 *
 * \verbatim
 * TITLE
 *  The following line must contain the title.
 *
 *
 * ABSTRACT
 *  The abstract is read in starting with the following line until an empty line is reached.
 *
 * COMMENTS
 *  The comments are read in starting with the following line until an empty line is reached.
 *
 *
 * LABELS
 *  One label must occupy each subsequent line until an empty line is reached. The number 
 *  of labels should preferably match the number of variables in the data.
 *
 * DATA
 *  On the same line as the DATA command, there must appear a comma-delimited list of variable 
 *  names. These names should be kept short (some applications truncate to 4 characters). The 
 *  numerical data is read in beginning with the following line until an empty line is reached.
 *
 *  In every line, there must appear exactly 3 comma-delimited fields for each specified name. 
 *  The first field is the value, the second is the positive error, and the third is the negative 
 *  error. If you leave the last field blank (the comma is still required), then the positive error 
 *  will be interpreted as a symmetric error. If you include only the value, then the errors will be 
 *  set to zero.
 *
 * To reiterate, if you provide a DATA command with 2 names, e.g. "DATA 1st, 2nd", then every subsequent 
 * line must contain 2*3 - 1 = 5 commas. If you give 3 names, then there must be 3*3 - 1 = 8 commas.
 * 
 * Example 
 * TITLE
 *  Most Excellent Fake Data
 *
 * ABSTRACT
 *  This data was collected over
 *  many minutes of light work
 *  that was required to make it up.
 *
 * COMMENTS
 *  This data was generated using C++.
 *  The file was formatted with Emacs.
 *
 * LABEL
 *  Randomized Linear
 *  Randomized Gaussian
 *  Randomized Lorentzian
 *  Run
 *
 * DATA line, gauss, lrntz, run
 * -1.966, -0.168, -0.106,  0.048, 0.002, 0.005,  0.184, 0.010, 0.017, 1001, , , run 1001 title
 * -1.895, -0.151, -0.128,  0.014, 0.001, 0.001,  0.259, 0.017, 0.015, 1002, , , run 1002 title
 * -1.836, -0.127, -0.184,  0.013, 0.001, 0.001,  0.202, 0.017, 0.020, 1003, , , run 1003 title
 * -1.739, -0.064, -0.166,  0.057, 0.003, 0.004,  0.237, 0.016, 0.018, 1004, , , run 1004 title
 * -1.601, -0.062, -0.147,  0.104, 0.008, 0.006,  0.271, 0.012, 0.025, 1005, , , run 1005 title
 *  .        .        .          .        .        .          .        .        .
 *  .        .        .          .        .        .          .        .        .
 *  .        .        .          .        .        .          .        .        .
 * Alternatively, the data often utilizes the continuation character ('\') and is labelled like
 * DATA line, gauss, lrntz
 * linear = -1.966,  -0.168,  -0.106, \
 * gaussn =  0.048,   0.002,   0.005, \
 * lorntz =  0.184,   0.010,   0.017, \
 * 1001,,, run 1001 title
 * linear = -1.895,  -0.151,  -0.128, \
 * gaussn =  0.014,   0.001,   0.001, \
 * lorntz =  0.259,   0.017,   0.015, |
 * 1002,,, run 1002 title
 * linear = -1.836,  -0.127,  -0.184, \
 * gaussn =  0.013,   0.001,   0.001, \
 * lorntz =  0.202,   0.017,   0.020, |
 * 1003,,, run 1003 title
 *   .        .        .        .
 *   .        .        .        .
 *   .        .        .        .
 * If there is a run line as in the above examples, it must be at the end of the data and given
 * in this just slight odd manner (do not blame me, I haven't invented this format ;-) ).
 * \endverbatim
 *
 * <p><b>WARNING:</b>For the row like data representation (the top DATA representation shown) 
 * it is essential that there are always at least one space inbetween commas (the reason is that
 * I am too lazy to write my own tokenizer), e.g.
 *
 * \verbatim
 * -1.966, -0.168, ,  0.048, , ,  0.184, 0.010, 0.017, 1001, , , run 1001 title
 * \endverbatim
 *
 * <p>which is ok, but
 *
 * \verbatim
 * -1.966, -0.168,,  0.048,,,  0.184, 0.010, 0.017, 1001,,, run 1001 title
 * \endverbatim
 *
 * <p>will <b>not</b> work!
 *
 * <p>Some db-files do have a '\\-e' or '\\e' label just between the DATA tag line and the real data.
 * This tag will just be ignored.
 *
 * <b>return:</b>
 * - true at successful reading,
 * - otherwise false.
 */
Bool_t PRunDataHandler::ReadDBFile()
{
  Bool_t success = true;

  // open file
  ifstream f;

  // open db-file
  f.open(fRunPathName.Data(), ifstream::in);
  if (!f.is_open()) {
    cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** Couldn't open data file (" << fRunPathName.Data() << ") for reading, sorry ...";
    cerr << endl;
    return false;
  }

  PRawRunData runData;

  runData.fDataNonMusr.SetFromAscii(false);

  Int_t     lineNo = 0;
  Int_t     idx;
  Int_t     dbTag = -1;
  Char_t    instr[512];
  TString line, workStr;
  Double_t  val;
  Bool_t firstData = true; // needed as a switch to check in which format the data are given.
  Bool_t labelledFormat = true; // flag showing if the data are given in row format, or as labelled format (see description above, default is labelled format)
  Bool_t dataTagsRead = false; // flag showing if the data tags are alread read

  // variables needed to tokenize strings
  TString tstr;
  TObjString *ostr;
  TObjArray *tokens = 0;

  while (!f.eof()) {
    // get next line from file
    f.getline(instr, sizeof(instr));
    line = TString(instr);
    lineNo++;

    // check if comment line
    if (line.BeginsWith("#") || line.BeginsWith("%"))
      continue;

    // ignore empty lines
    if (line.IsWhitespace())
      continue;

    // check for db specific tags
    workStr = line;
    workStr.Remove(TString::kLeading, ' '); // remove spaces from the begining
    if (workStr.BeginsWith("title", TString::kIgnoreCase)) {
      dbTag = 0;
      continue;
    } else if (workStr.BeginsWith("abstract", TString::kIgnoreCase)) {
      dbTag = 1;
      continue;
    } else if (workStr.BeginsWith("comments", TString::kIgnoreCase)) {
      dbTag = 2;
      continue;
    } else if (workStr.BeginsWith("label", TString::kIgnoreCase)) {
      dbTag = 3;
      continue;
    } else if (workStr.BeginsWith("data", TString::kIgnoreCase) && !dataTagsRead) {
      dataTagsRead = true;
      dbTag = 4;

      // filter out all data tags
      tokens = workStr.Tokenize(" ,\t");
      for (Int_t i=1; i<tokens->GetEntries(); i++) {
        ostr = dynamic_cast<TObjString*>(tokens->At(i));
        runData.fDataNonMusr.AppendDataTag(ostr->GetString());
      }

      // clean up tokens
      if (tokens) {
        delete tokens;
        tokens = 0;
      }
      continue;
    }

    switch (dbTag) {
      case 0: // TITLE
        runData.SetRunTitle(workStr);
        break;
      case 1: // ABSTRACT
        // nothing to be done for now
        break;
      case 2: // COMMENTS
        // nothing to be done for now
        break;
      case 3: // LABEL
        runData.fDataNonMusr.AppendLabel(workStr);
        break;
      case 4: // DATA
        // filter out potential start data tag
        if (workStr.BeginsWith("\\-e", TString::kIgnoreCase) ||
            workStr.BeginsWith("\\e", TString::kIgnoreCase)  ||
            workStr.BeginsWith("/-e", TString::kIgnoreCase)  ||
            workStr.BeginsWith("/e", TString::kIgnoreCase)) {
          continue;
        }

        // check if first real data line
        if (firstData) {
          // check if data are given just as rows are as labelled columns (see description above)
          tokens = workStr.Tokenize(",");
          ostr = dynamic_cast<TObjString*>(tokens->At(0));
          if (!ostr->GetString().IsFloat()) {
            labelledFormat = true;
          } else {
            labelledFormat = false;
          }
          // clean up tokens
          if (tokens) {
            delete tokens;
            tokens = 0;
          }

          // prepare data vector for use
          PDoubleVector dummy;
          for (UInt_t i=0; i<runData.fDataNonMusr.GetDataTags()->size(); i++) {
            runData.fDataNonMusr.AppendData(dummy);
            runData.fDataNonMusr.AppendErrData(dummy);
          }

          firstData = false;
        }

        if (labelledFormat) { // handle labelled formated data
          // check if run line
          const Char_t *str = workStr.Data();
          if (isdigit(str[0])) { // run line
            TString run("run");
            idx = GetDataTagIndex(run, runData.fDataNonMusr.GetDataTags());
            if (idx == -1) {
              cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
              cerr << endl << ">> " << workStr.Data();
              cerr << endl << ">> found potential run data line without run data tag.";
              return false;
            }
            // split string in tokens
            tokens = workStr.Tokenize(","); // line has structure: runNo,,, runTitle
            ostr = dynamic_cast<TObjString*>(tokens->At(0));
            tstr = ostr->GetString();
            if (!tstr.IsFloat()) {
              cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
              cerr << endl << ">> " << workStr.Data();
              cerr << endl << ">> Expected db-data line with structure: runNo,,, runTitle";
              cerr << endl << ">> runNo = " << tstr.Data() << ", seems to be not a number.";
              delete tokens;
              return false;
            }
            val = tstr.Atof();
            runData.fDataNonMusr.AppendSubData(idx, val);
            runData.fDataNonMusr.AppendSubErrData(idx, 1.0);
          } else { // tag = data line
            // remove all possible spaces
            workStr.ReplaceAll(" ", "");
            // split string in tokens
            tokens = workStr.Tokenize("=,"); // line has structure: tag = val,err1,err2,
            if (tokens->GetEntries() < 3) {
              cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
              cerr << endl << ">> " << workStr.Data();
              cerr << endl << ">> Expected db-data line with structure: tag = val,err1,err2,\\";
              delete tokens;
              return false;
            }
            ostr = dynamic_cast<TObjString*>(tokens->At(0));
            tstr = ostr->GetString();
            idx = GetDataTagIndex(tstr, runData.fDataNonMusr.GetDataTags());
            if (idx == -1) {
              cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
              cerr << endl << ">> " << workStr.Data();
              cerr << endl << ">> data tag error: " << tstr.Data() << " seems not present in the data tag list";
              delete tokens;
              return false;
            }

            switch (tokens->GetEntries()) {
              case 3: // tag = val,,,
                ostr = dynamic_cast<TObjString*>(tokens->At(1));
                tstr = ostr->GetString();
                if (!tstr.IsFloat()) {
                  cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
                  cerr << endl << ">> " << workStr.Data();
                  cerr << endl << ">> Expected db-data line with structure: tag = val,err1,err2,\\";
                  cerr << endl << ">> val = " << tstr.Data() << ", seems to be not a number.";
                  delete tokens;
                  return false;
                }
                val = tstr.Atof();
                runData.fDataNonMusr.AppendSubData(idx, val);
                runData.fDataNonMusr.AppendSubErrData(idx, 1.0);
                break;
              case 4: // tag = val,err,,
              case 5: // tag = val,err1,err2,
                // handle val
                ostr = dynamic_cast<TObjString*>(tokens->At(1));
                tstr = ostr->GetString();
                if (!tstr.IsFloat()) {
                  cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
                  cerr << endl << ">> " << workStr.Data();
                  cerr << endl << ">> Expected db-data line with structure: tag = val,err1,err2,\\";
                  cerr << endl << ">> val = " << tstr.Data() << ", seems to be not a number.";
                  delete tokens;
                  return false;
                }
                val = tstr.Atof();
                runData.fDataNonMusr.AppendSubData(idx, val);
                // handle err1 (err2 will be ignored for the time being)
                ostr = dynamic_cast<TObjString*>(tokens->At(2));
                tstr = ostr->GetString();
                if (!tstr.IsFloat()) {
                  cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
                  cerr << endl << ">> " << workStr.Data();
                  cerr << endl << ">> Expected db-data line with structure: tag = val,err1,err2,\\";
                  cerr << endl << ">> err1 = " << tstr.Data() << ", seems to be not a number.";
                  delete tokens;
                  return false;
                }
                val = tstr.Atof();
                runData.fDataNonMusr.AppendSubErrData(idx, val);
                break;
              default:
                cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
                cerr << endl << ">> " << workStr.Data();
                cerr << endl << ">> Expected db-data line with structure: tag = val,err1,err2,\\";
                delete tokens;
                return false;
                break;
            }
          }

        } else { // handle row formated data
          // split string in tokens
          tokens = workStr.Tokenize(","); // line has structure: val1, err11, err12, ..., valn, errn1, errn2, runNo, , , , runTitle
          if (tokens->GetEntries() != (Int_t)(3*runData.fDataNonMusr.GetDataTags()->size()+1)) {
            cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
            cerr << endl << ">> " << workStr.Data();
            cerr << endl << ">> Expected db-data line with structure: val1, err11, err12, ..., valn, errn1, errn2, runNo, , , , runTitle";
            cerr << endl << ">> found = " << tokens->GetEntries() << " tokens, however expected " << 3*runData.fDataNonMusr.GetDataTags()->size()+1;
            cerr << endl << ">> Perhaps there are commas without space inbetween, like 12.3,, 3.2,...";
            delete tokens;
            return false;
          }
          // extract data
          Int_t j=0;
          for (Int_t i=0; i<tokens->GetEntries()-1; i+=3) {
            // handle value
            ostr = dynamic_cast<TObjString*>(tokens->At(i));
            tstr = ostr->GetString();
            if (!tstr.IsFloat()) {
              cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
              cerr << endl << ">> " << workStr.Data();
              cerr << endl << ">> Expected db-data line with structure: val1, err11, err12, ..., valn, errn1, errn2, runNo, , , , runTitle";
              cerr << endl << ">> value=" << tstr.Data() << " seems not to be a number";
              delete tokens;
              return false;
            }
            runData.fDataNonMusr.AppendSubData(j, tstr.Atof());

            // handle 1st error if present (2nd will be ignored for now)
            ostr = dynamic_cast<TObjString*>(tokens->At(i+1));
            tstr = ostr->GetString();
            if (tstr.IsWhitespace()) {
              runData.fDataNonMusr.AppendSubErrData(j, 1.0);
            } else if (tstr.IsFloat()) {
              runData.fDataNonMusr.AppendSubErrData(j, tstr.Atof());
            } else {
              cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo << ":";
              cerr << endl << ">> " << workStr.Data();
              cerr << endl << ">> Expected db-data line with structure: val1, err11, err12, ..., valn, errn1, errn2, runNo, , , , runTitle";
              cerr << endl << ">> error1=" << tstr.Data() << " seems not to be a number";
              delete tokens;
              return false;
            }
            j++;
          }
        }
        break;
      default:
        break;
    }
  }

  f.close();

  // check that the number of labels == the number of data tags
  if (runData.fDataNonMusr.GetLabels()->size() != runData.fDataNonMusr.GetDataTags()->size()) {
    cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR**";
    cerr << endl << ">> number of LABELS found    = " << runData.fDataNonMusr.GetLabels()->size();
    cerr << endl << ">> number of Data tags found = " << runData.fDataNonMusr.GetDataTags()->size();
    cerr << endl << ">> They have to be equal!!";
    if (tokens) {
      delete tokens;
      tokens = 0;
    }
    return false;
  }

  // check if all vectors have the same size
  for (UInt_t i=1; i<runData.fDataNonMusr.GetData()->size(); i++) {
    if (runData.fDataNonMusr.GetData()->at(i).size() != runData.fDataNonMusr.GetData()->at(i-1).size()) {
      cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo;
      cerr << endl << ">> label: " << runData.fDataNonMusr.GetDataTags()->at(i-1).Data() << ", number data elements = " << runData.fDataNonMusr.GetData()->at(i-1).size();
      cerr << endl << ">> label: " << runData.fDataNonMusr.GetDataTags()->at(i).Data() << ", number data elements = " << runData.fDataNonMusr.GetData()->at(i).size();
      cerr << endl << ">> They have to be equal!!";
      success = false;
      break;
    }
    if (runData.fDataNonMusr.GetErrData()->at(i).size() != runData.fDataNonMusr.GetErrData()->at(i-1).size()) {
      cerr << endl << ">> PRunDataHandler::ReadDBFile **ERROR** in line no " << lineNo;
      cerr << endl << ">> label: " << runData.fDataNonMusr.GetDataTags()->at(i-1).Data() << ", number data elements = " << runData.fDataNonMusr.GetData()->at(i-1).size();
      cerr << endl << ">> label: " << runData.fDataNonMusr.GetDataTags()->at(i).Data() << ", number error data elements = " << runData.fDataNonMusr.GetErrData()->at(i).size();
      cerr << endl << ">> They have to be equal!!";
      success = false;
      break;
    }
  }

  // clean up tokens
  if (tokens) {
    delete tokens;
    tokens = 0;
  }

  // keep run name
  runData.SetRunName(fRunName);

  fData.push_back(runData);

  return success;
}

//--------------------------------------------------------------------------
// WriteRootFile
//--------------------------------------------------------------------------
/**
 * <p> Write the PSI LEM root-file format.
 *
 * <b>return:</b>
 * - true on successful writting,
 * - otherwise false.
 *
 * \param fln file name. If empty, the routine will try to construct one
 */
Bool_t PRunDataHandler::WriteRootFile(TString fln)
{
  // generate output file name if needed
  if (!fAny2ManyInfo->useStandardOutput || (fAny2ManyInfo->compressionTag > 0)) {
    if (fln.Length() == 0) {
      Bool_t ok = false;
      fln = GetFileName(".root", ok);
      if (!ok)
        return false;
    } else {
      fln.Prepend(fAny2ManyInfo->outPath);
    }
    // keep the file name if compression is whished
    fAny2ManyInfo->outPathFileName.push_back(fln);
  } else {
    fln = fAny2ManyInfo->outPath + TString("__tmp.root");
  }

  if (!fAny2ManyInfo->useStandardOutput)
    cout << endl << ">> PRunDataHandler::WriteRootFile(): writing a root data file (" << fln.Data() << ") ... " << endl;

  // generate data file
  TFolder *histosFolder;
  TFolder *decayAnaModule;
  TFolder *runInfo;

  histosFolder = gROOT->GetRootFolder()->AddFolder("histos", "Histograms");
  gROOT->GetListOfBrowsables()->Add(histosFolder, "histos");
  decayAnaModule = histosFolder->AddFolder("DecayAnaModule", "muSR decay histograms");

  runInfo = gROOT->GetRootFolder()->AddFolder("RunInfo", "LEM RunInfo");
  gROOT->GetListOfBrowsables()->Add(runInfo, "RunInfo");
  TLemRunHeader *header = new TLemRunHeader();
  gROOT->GetListOfBrowsables()->Add(runInfo, "RunInfo");

  // feed header info
  header->SetRunTitle(fData[0].GetRunTitle()->Data());
  header->SetLemSetup(fData[0].GetSetup()->Data());
  header->SetRunNumber(fData[0].GetRunNumber());
  TString dt = *fData[0].GetStartDate() + "/" + *fData[0].GetStartTime();
  header->SetStartTimeString(dt.Data());
  dt = *fData[0].GetStopDate() + "/" + *fData[0].GetStopTime();
  header->SetStopTimeString(dt.Data());
  header->SetStartTime(fData[0].GetStartDateTime());
  header->SetStopTime(fData[0].GetStopDateTime());
  header->SetModeratorHV(-999.9, 0.0);
  header->SetSampleHV(-999.9, 0.0);
  header->SetImpEnergy(-999.9);
  header->SetSampleTemperature(fData[0].GetTemperature(0), fData[0].GetTempError(0));
  header->SetSampleBField(fData[0].GetField(), 0.0);
  header->SetTimeResolution(fData[0].GetTimeResolution());
  header->SetNChannels(static_cast<UInt_t>(fData[0].GetDataBin(0)->size()/fAny2ManyInfo->rebin));
  header->SetNHist(fData[0].GetNoOfHistos());
  header->SetCuts("none");
  header->SetModerator("none");
  // feed t0's
  Double_t *tt0 = new Double_t[fData[0].GetNoOfHistos()];
  for (UInt_t i=0;  i<fData[0].GetNoOfHistos(); i++)
    tt0[i] = 0.0;
  if (fData[0].GetT0Size() > 0) {
    for (UInt_t i=0; i<fData[0].GetT0Size(); i++) {
      tt0[i] = fData[0].GetT0(i)/fAny2ManyInfo->rebin;
    }
  }
  header->SetTimeZero(tt0);
  runInfo->Add(header); //add header to RunInfo folder

  // feed histos
  vector<TH1F*> histos;
  TH1F *histo = 0;
  Char_t str[32];

  if (fAny2ManyInfo->rebin == 1) {
     for (UInt_t i=0; i<fData[0].GetNoOfHistos(); i++) {
       if (i<10)
         sprintf(str, "hDecay0%d", (Int_t)i);
       else
         sprintf(str, "hDecay%d", (Int_t)i);
       histo = new TH1F(str, str, static_cast<UInt_t>(fData[0].GetDataBin(0)->size())+1, -0.5, static_cast<UInt_t>(fData[0].GetDataBin(0)->size()+0.5));
       for (UInt_t j=0; j<fData[0].GetDataBin(i)->size(); j++) {
         histo->SetBinContent(j+1, fData[0].GetDataBin(i)->at(j));
       }
       histos.push_back(histo);
     }
  } else { // rebin > 1
    UInt_t dataRebin = 0;
    UInt_t dataCount = 0;
    for (UInt_t i=0; i<fData[0].GetNoOfHistos(); i++) {
      if (i<10)
        sprintf(str, "hDecay0%d", (Int_t)i);
      else
        sprintf(str, "hDecay%d", (Int_t)i);
      histo = new TH1F(str, str, static_cast<UInt_t>(fData[0].GetDataBin(0)->size()/fAny2ManyInfo->rebin)+1, -0.5, static_cast<UInt_t>(fData[0].GetDataBin(0)->size()/fAny2ManyInfo->rebin+0.5));
      dataCount = 0;
      for (UInt_t j=0; j<fData[0].GetDataBin(i)->size(); j++) {
        if ((j > 0) && (j % fAny2ManyInfo->rebin == 0)) {
          dataCount++;
          histo->SetBinContent(dataCount, dataRebin);
          dataRebin = 0;
        }
        dataRebin += static_cast<UInt_t>(fData[0].GetDataBin(i)->at(j));
      }      
      histos.push_back(histo);
    }
  }

  // add histos to the DecayAnaModule folder
  for (UInt_t i=0; i<histos.size(); i++)
    decayAnaModule->Add(histos[i]);

  // write file
  TFile *fout = new TFile(fln, "RECREATE", fln);
  if (fout == 0) {
    cerr << endl << "PRunDataHandler::WriteRootFile(): **ERROR** Couldn't create ROOT file '" << fln << "'" << endl;
    return false;
  }

  fout->cd();
  runInfo->Write();
  histosFolder->Write();
  fout->Close();

  // clean up
  for (UInt_t i=0; i<histos.size(); i++) {
    delete histos[i];
  }
  histos.clear();
  delete fout;
  delete [] tt0;
  delete header;

  // check if root file shall be streamed to stdout
  if (fAny2ManyInfo->useStandardOutput && (fAny2ManyInfo->compressionTag == 0)) {
    // stream file to stdout
    ifstream is;
    int length=1024;
    char *buffer;

    is.open(fln.Data(), ios::binary);
    if (!is.is_open()) {
      cerr << endl << "PRunDataHandler::WriteRootFile(): **ERROR** Couldn't open the root-file for streaming." << endl;
      remove(fln.Data());
      return false;
    }

    // get length of file
    is.seekg(0, ios::end);
    length = is.tellg();
    is.seekg(0, ios::beg);

    if (length == -1) {
      cerr << endl << "PRunDataHandler::WriteRootFile(): **ERROR** Couldn't determine the root-file size." << endl;
      remove(fln.Data());
      return false;
    }

    // allocate memory
    buffer = new char [length];

    // read data as a block
    while (!is.eof()) {
      is.read(buffer, length);
      cout.write(buffer, length);
    }

    is.close();

    delete [] buffer;

    // delete temporary root file
    remove(fln.Data());
  }

  return true;
}

//--------------------------------------------------------------------------
// WriteNexusFile
//--------------------------------------------------------------------------
/**
 * <p> Write the nexus-file format.
 *
 * <b>return:</b>
 * - true on successful writting,
 * - otherwise false.
 *
 * \param fln file name. If empty, the routine will try to construct one
 */
Bool_t PRunDataHandler::WriteNexusFile(TString fln)
{  
#ifdef PNEXUS_ENABLED
  // generate output file name
  if (fln.Length() == 0) {
    Bool_t ok = false;
    fln = GetFileName(".nxs", ok);
    if (!ok)
      return false;
  } else {
    fln.Prepend(fAny2ManyInfo->outPath);
  }
  // keep the file name if compression is whished
  fAny2ManyInfo->outPathFileName.push_back(fln);

  if (!fAny2ManyInfo->useStandardOutput)
    cout << endl << ">> PRunDataHandler::WriteNexusFile(): writing a NeXus data file (" << fln.Data() << ") ... " << endl;

  // create NeXus object
  PNeXus *nxs = new PNeXus();
  if (nxs == 0) {
    cerr << endl << ">> PRunDataHandler::WriteNexusFile(): **ERROR** couldn't invoke the NeXus object." << endl;
    return false;
  }

  // fill necessary data structures
  nxs->SetFileName(fln.Data());
  nxs->SetIDFVersion(1);
  nxs->SetProgramName("any2many");
  nxs->SetProgramVersion("$Id$");
  nxs->SetRunNumber(fData[0].GetRunNumber());
  nxs->SetRunTitle(fData[0].GetRunTitle()->Data());
  nxs->SetRunNotes("n/a");
  nxs->SetAnalysisTag("n/a");
  nxs->SetLab("PSI");
  nxs->SetBeamline("n/a");
  nxs->SetStartDate(fData[0].GetStartDate()->Data());
  nxs->SetStartTime(fData[0].GetStartTime()->Data());
  nxs->SetStopDate(fData[0].GetStopDate()->Data());
  nxs->SetStopTime(fData[0].GetStopTime()->Data());
  nxs->SetSwitchingState(0);
  nxs->SetUser("n/a");
  nxs->SetExperimentNumber("n/a");
  nxs->SetSampleName(fData[0].GetSample()->Data());
  nxs->SetSampleTemperature(fData[0].GetTemperature(0));
  nxs->SetSampleTemperatureUints("Kelvin");
  nxs->SetMagneticField(fData[0].GetField());
  nxs->SetMagneticFieldUnits("Gauss");
  nxs->SetSampleEnvironment("n/a");
  nxs->SetSampleShape("n/a");
  nxs->SetMagneticFieldVectorAvailable(0);
  nxs->SetExperimentName("n/a");
  nxs->SetNoDetectors(fData[0].GetNoOfHistos());
  nxs->SetCollimatorType("n/a");
  nxs->SetTimeResolution(fData[0].GetTimeResolution());
  if (fData[0].GetT0(0) == -1)
    nxs->SetT0(0);
  else
    nxs->SetT0(fData[0].GetT0(0)); // this needs to be changed in the long term, since for continous sources each detector has its one t0!!
  if (fData[0].GetGoodDataBin(0).first == -1) {
    nxs->SetFirstGoodBin(0);
    nxs->SetLastGoodBin(0);
  } else {
    nxs->SetFirstGoodBin(fData[0].GetGoodDataBin(0).first);
    nxs->SetLastGoodBin(fData[0].GetGoodDataBin(0).second);
  }
  // feed real histogram data
  PUIntVector data;
  for (UInt_t i=0; i<fData[0].GetNoOfHistos(); i++) {
    for (UInt_t j=0; j<fData[0].GetDataBin(i)->size(); j++) {
      data.push_back((UInt_t)fData[0].GetDataBin(i)->at(j));
    }
    nxs->SetHisto(i, data);
    data.clear();
  }

  // filter out the proper file type, i.e. HDF4, HDF5, or XML
  char fileType[32];
  memset(fileType, '\0', 32);
  if (!fAny2ManyInfo->outFormat.CompareTo("nexus-hdf4", TString::kIgnoreCase))
    strncpy(fileType, "hdf4", sizeof(fileType));
  else if (!fAny2ManyInfo->outFormat.CompareTo("nexus-hdf5", TString::kIgnoreCase))
    strncpy(fileType, "hdf5", sizeof(fileType));
  else if (!fAny2ManyInfo->outFormat.CompareTo("nexus-xml", TString::kIgnoreCase))
    strncpy(fileType, "xml", sizeof(fileType));
  else {
    cerr << endl << ">> PRunDataHandler::WriteNexusFile(): **ERROR** undefined output NeXus format " << fAny2ManyInfo->outFormat.Data() << " found.";
    cerr << endl << ">>   Allowed are: hdf4, hdf5, xml" << endl;
    if (nxs != 0) {
      delete nxs;
      nxs = 0;
    }
    return false;
  }

  // write file
  nxs->WriteFile(fln, fileType);

  // clean up
  if (nxs != 0) {
    delete nxs;
    nxs = 0;
  }
#else
  cout << endl << ">> PRunDataHandler::WriteNexusFile(): Sorry, not enabled at configuration level, i.e. --enable-NeXus when executing configure" << endl << endl;
#endif
  return true;
}

//--------------------------------------------------------------------------
// WriteWkmFile
//--------------------------------------------------------------------------
/**
 * <p> Write the wkm-file format.
 *
 * <b>return:</b>
 * - true on successful writting,
 * - otherwise false.
 *
 * \param fln file name. If empty, the routine will try to construct one
 */
Bool_t PRunDataHandler::WriteWkmFile(TString fln)
{
  // generate output file name
  if (fln.Length() == 0) {
    Bool_t ok = false;
    fln = GetFileName(".wkm", ok);
    if (!ok)
      return false;
  } else {
    fln.Prepend(fAny2ManyInfo->outPath);
  }
  // keep the file name if compression is whished
  fAny2ManyInfo->outPathFileName.push_back(fln);

  if (!fAny2ManyInfo->useStandardOutput)
    cout << endl << ">> PRunDataHandler::WriteWkmFile(): writing a wkm data file (" << fln.Data() << ") ... " << endl;

  // write ascii file
  ofstream fout;
  streambuf* strm_buffer = 0;

  if (!fAny2ManyInfo->useStandardOutput || (fAny2ManyInfo->compressionTag > 0)) {
    // open data-file
    fout.open(fln.Data(), ofstream::out);
    if (!fout.is_open()) {
      cerr << endl << ">> PRunDataHandler::WriteWkmFile **ERROR** Couldn't open data file (" << fln.Data() << ") for writing, sorry ...";
      cerr << endl;
      return false;
    }

    // save output buffer of the stream
    strm_buffer = cout.rdbuf();

    // redirect output into the file
    cout.rdbuf(fout.rdbuf());
  }

  // write header
  cout << "- WKM data file converted with any2many";
  cout << endl << "Run         : " << fData[0].GetRunNumber();
  if (fData[0].GetRunTitle()->Length() > 0)
    cout << endl << "Title       : " << fData[0].GetRunTitle()->Data();
  if (fData[0].GetNoOfTemperatures() == 1) {
    cout << endl << "Temp        : " << fData[0].GetTemperature(0);
  } else if (fData[0].GetNoOfTemperatures() > 1) {
    cout << endl << "Temp(meas1) : " << fData[0].GetTemperature(0) << " +- " << fData[0].GetTempError(0);
    cout << endl << "Temp(meas2) : " << fData[0].GetTemperature(1) << " +- " << fData[0].GetTempError(1);
  } else {
    cout << endl << "Temp        : ??";
  }
  if (fData[0].GetField() != PMUSR_UNDEFINED) {
    cout << endl << "Field       : " << fData[0].GetField();
  } else {
    cout << endl << "Field       : ??";
  }
  cout << endl << "Date        : " << fData[0].GetStartTime()->Data() << " " << fData[0].GetStartDate()->Data() << " / " << fData[0].GetStopTime()->Data() << " " << fData[0].GetStopDate()->Data();
  cout << endl << "Setup       : " << fData[0].GetSetup()->Data();
  cout << endl << "Groups      : " << fData[0].GetNoOfHistos();
  cout << endl << "Channels    : " << static_cast<UInt_t>(fData[0].GetDataBin(0)->size()/fAny2ManyInfo->rebin);
  cout.precision(10);
  cout << endl << "Resolution  : " << fData[0].GetTimeResolution()*fAny2ManyInfo->rebin/1.0e3; // ns->us
  cout.setf(ios::fixed,ios::floatfield);   // floatfield set to fixed

  // write data
  if (fAny2ManyInfo->rebin == 1) {
    for (UInt_t i=0; i<fData[0].GetNoOfHistos(); i++) {
      cout << endl << endl;
      for (UInt_t j=0; j<fData[0].GetDataBin(i)->size(); j++) {
        if ((j > 0) && (j % 16 == 0))
          cout << endl;
        cout << static_cast<Int_t>(fData[0].GetDataBin(i)->at(j)) << " ";
      }
    }
  } else { // rebin > 1
    Int_t dataRebin = 0;
    Int_t count = 0;
    for (UInt_t i=0; i<fData[0].GetNoOfHistos(); i++) {
      cout << endl << endl;
      count = 0;
      dataRebin = 0; // reset rebin
      for (UInt_t j=0; j<fData[0].GetDataBin(i)->size(); j++) {
        if ((j > 0) && (j % fAny2ManyInfo->rebin == 0)) {
          cout << dataRebin << " ";
          count++;
          dataRebin = 0;
          if ((count > 0) && (count % 16 == 0))
            cout << endl;
        } else {
          dataRebin += static_cast<Int_t>(fData[0].GetDataBin(i)->at(j));
        }
      }
    }
  }

  if (!fAny2ManyInfo->useStandardOutput || (fAny2ManyInfo->compressionTag > 0)) {
    // restore old output buffer
    cout.rdbuf(strm_buffer);

    fout.close();
  }

  return true;
}

//--------------------------------------------------------------------------
// WritePsiBinFile
//--------------------------------------------------------------------------
/**
 * <p> Write the psi-bin-file format.
 *
 * <b>return:</b>
 * - true on successful writting,
 * - otherwise false.
 *
 * \param fln file name. If empty, the routine will try to construct one
 */
Bool_t PRunDataHandler::WritePsiBinFile(TString fln)
{
  cout << endl << ">> PRunDataHandler::WritePsiBinFile(): will write a psi-bin data file. Not yet implemented ... " << endl;

  // generate output file name if needed
  if (!fAny2ManyInfo->useStandardOutput || (fAny2ManyInfo->compressionTag > 0)) {
    if (fln.Length() == 0) {
      Bool_t ok = false;
      fln = GetFileName(".bin", ok);
      if (!ok)
        return false;
    } else {
      fln.Prepend(fAny2ManyInfo->outPath);
    }
    // keep the file name if compression is whished
    fAny2ManyInfo->outPathFileName.push_back(fln);
  } else {
    fln = fAny2ManyInfo->outPath + TString("__tmp.bin");
  }

  if (!fAny2ManyInfo->useStandardOutput)
    cout << endl << ">> PRunDataHandler::WritePsiBinFile(): writing a psi-bin data file (" << fln.Data() << ") ... " << endl;

  MuSR_td_PSI_bin psibin;
  int status = 0;

  // fill header information
  // run number
  psibin.put_runNumber_int(fData[0].GetRunNumber());
  // length of histograms
  psibin.put_histoLength_bin((int)(fData[0].GetDataBin(0)->size()));
  // number of histograms
  psibin.put_numberHisto_int((int)fData[0].GetNoOfHistos());
  // run title = sample (10 char) / temp (10 char) / field (10 char) / orientation (10 char)
  char cstr[11];
  // sample
  if (fData[0].GetSample()->Length() > 0)
    strncpy(cstr, fData[0].GetSample()->Data(), 10);
  else
    strcpy(cstr, "??");
  cstr[10] = '\0';
  psibin.put_sample(cstr);
  // temp
  if (fData[0].GetNoOfTemperatures() > 0)
    snprintf(cstr, 10, "%.1f K", fData[0].GetTemperature(0));
  else
    strcpy(cstr, "?? K");
  cstr[10] = '\0';
  psibin.put_temp(cstr);
  // field
  if (fData[0].GetField() > 0)
    snprintf(cstr, 10, "%.1f G", fData[0].GetField());
  else
    strcpy(cstr, "?? G");
  cstr[10] = '\0';
  psibin.put_field(cstr);
  // orientation
  if (fData[0].GetOrientation()->Length() > 0)
    strncpy(cstr, fData[0].GetOrientation()->Data(), 10);
  else
    strcpy(cstr, "??");
  cstr[10] = '\0';
  psibin.put_orient(cstr);
  // setup
  if (fData[0].GetSetup()->Length() > 0)
    strncpy(cstr, fData[0].GetSetup()->Data(), 10);
  else
    strcpy(cstr, "??");
  cstr[10] = '\0';
  psibin.put_setup(cstr);

  // run start date
  vector<string> svec;
  strncpy(cstr, fData[0].GetStartDate()->Data(), 9);
  cstr[9] = '\0';
  svec.push_back(cstr);
  // run start time
  strncpy(cstr, fData[0].GetStartTime()->Data(), 8);
  cstr[8] = '\0';
  svec.push_back(cstr);
  psibin.put_timeStart_vector(svec);
  svec.clear();

  // run stop date
  strncpy(cstr, fData[0].GetStopDate()->Data(), 9);
  cstr[9] = '\0';
  svec.push_back(cstr);
  // run stop time
  strncpy(cstr, fData[0].GetStopTime()->Data(), 8);
  cstr[8] = '\0';
  svec.push_back(cstr);
  psibin.put_timeStop_vector(svec);
  svec.clear();

  // t0's
  for (UInt_t i=0; i<fData[0].GetT0Size(); i++)
    psibin.put_t0_int(i, fData[0].GetT0(i));

  // first/last good bin
  for (UInt_t i=0; i<fData[0].GetNoOfHistos(); i++) {
    psibin.put_firstGood_int(i, fData[0].GetGoodDataBin(i).first);
    psibin.put_lastGood_int(i, fData[0].GetGoodDataBin(i).second);
  }

  // number of measured temperatures
  psibin.put_numberTemperature_int(fData[0].GetNoOfTemperatures());

  // mean temperatures
  vector<double> dvec;
  for (UInt_t i=0; i<fData[0].GetNoOfTemperatures(); i++)
    dvec.push_back(fData[0].GetTemperature(i));
  psibin.put_temperatures_vector(dvec);

  // standard deviation of temperatures
  dvec.clear();
  for (UInt_t i=0; i<fData[0].GetNoOfTemperatures(); i++)
    dvec.push_back(fData[0].GetTempError(i));
  psibin.put_devTemperatures_vector(dvec);

  // write comment
  psibin.put_comment(fData[0].GetRunTitle()->Data());

  // write histogram labels
  vector<string> histoLabel;
  histoLabel.resize(fData[0].GetNoOfHistos());
  char hl[32];
  for (UInt_t i=0; i<fData[0].GetNoOfHistos(); i++) {
    sprintf(hl, "h%d", i);
    histoLabel[i] = hl;
  }
  psibin.put_histoNames_vector(histoLabel);

  // write time resolution
  psibin.put_binWidth_ns(fData[0].GetTimeResolution());

  // write scaler dummies
  psibin.put_numberScaler_int(0);

  // fill histograms
  vector< vector<int> > histo;
  histo.resize(fData[0].GetNoOfHistos());
  for (UInt_t i=0; i<fData[0].GetNoOfHistos(); i++) {
    for (UInt_t j=0; j<fData[0].GetDataBin(i)->size(); j++)
      histo[i].push_back((Int_t)fData[0].GetDataBin(i)->at(j));
  }
  status = psibin.put_histo_array_int(histo);
  if (status != 0) {
    cerr << endl << ">> PRunDataHandler::WritePsiBinFile(): " << psibin.ConsistencyStatus() << endl;
    return false;
  }

  if (!psibin.CheckDataConsistency()) {
    cerr << endl << ">> PRunDataHandler::WritePsiBinFile(): " << psibin.ConsistencyStatus() << endl;
    return false;
  }

  // write data to file
  status = psibin.write(fln.Data());

  if (status != 0) {
    cerr << endl << ">> PRunDataHandler::WritePsiBinFile(): " << psibin.WriteStatus() << endl;
    return false;
  }

  return true;
}

//--------------------------------------------------------------------------
// WriteMudFile
//--------------------------------------------------------------------------
/**
 * <p> Write the mud-file format.
 *
 * <b>return:</b>
 * - true on successful writting,
 * - otherwise false.
 *
 * \param fln file name. If empty, the routine will try to construct one
 */
Bool_t PRunDataHandler::WriteMudFile(TString fln)
{
  // generate output file name if needed
  if (!fAny2ManyInfo->useStandardOutput || (fAny2ManyInfo->compressionTag > 0)) {
    if (fln.Length() == 0) {
      Bool_t ok = false;
      fln = GetFileName(".msr", ok);
      if (!ok)
        return false;
    } else {
      fln.Prepend(fAny2ManyInfo->outPath);
    }
    // keep the file name if compression is whished
    fAny2ManyInfo->outPathFileName.push_back(fln);
  } else {
    fln = TString("__tmp.msr");
  }

  if (!fAny2ManyInfo->useStandardOutput)
    cout << endl << ">> PRunDataHandler::WriteMudFile(): writing a mud data file (" << fln.Data() << ") ... " << endl;

  // generate the mud data file
  int fd = MUD_openWrite((char*)fln.Data(), MUD_FMT_TRI_TD_ID);
  if (fd == -1) {
    cerr << endl << ">> PRunDataHandler::WriteMudFile(): **ERROR** couldn't open mud data file for write ..." << endl;
    return false;
  }

  // generate header information
  char dummy[32], info[128];
  strcpy(dummy, "???");
  MUD_setRunDesc(fd, MUD_SEC_GEN_RUN_DESC_ID);
  MUD_setExptNumber(fd, 0);
  MUD_setRunNumber(fd, fData[0].GetRunNumber());
  MUD_setElapsedSec(fd, 0);
  MUD_setTimeBegin(fd, fData[0].GetStartDateTime());
  MUD_setTimeEnd(fd, fData[0].GetStopDateTime());
  MUD_setTitle(fd, (char *)fData[0].GetRunTitle()->Data());
  MUD_setLab(fd, dummy);
  MUD_setArea(fd, dummy);
  MUD_setMethod(fd, (char *)fData[0].GetSetup()->Data());
  MUD_setApparatus(fd, dummy);
  MUD_setInsert(fd, dummy);
  MUD_setSample(fd, dummy);
  MUD_setOrient(fd, dummy);
  MUD_setDas(fd, dummy);
  MUD_setExperimenter(fd, dummy);
  sprintf(info, "%lf+-%lf (K)", fData[0].GetTemperature(0), fData[0].GetTempError(0));
  MUD_setTemperature(fd, info);
  sprintf(info, "%lf", fData[0].GetField());
  MUD_setField(fd, info);

  // generate the histograms
  MUD_setHists(fd, MUD_GRP_TRI_TD_HIST_ID, fData[0].GetNoOfHistos());

  UInt_t *data, dataSize = fData[0].GetDataBin(0)->size()/fAny2ManyInfo->rebin + 1;
  data = new UInt_t[dataSize];
  if (data == 0) {
    cerr << endl << ">> PRunDataHandler::WriteMudFile(): **ERROR** couldn't allocate memory for the data ..." << endl;
    MUD_closeWrite(fd);
    return false;
  }

  UInt_t noOfEvents = 0, ival = 0, k = 0;
  for (UInt_t i=0; i<fData[0].GetNoOfHistos(); i++) {

    // fill data
    for (UInt_t j=0; j<dataSize; j++)
      data[j] = 0;
    noOfEvents = 0;
    k = 0;
    for (UInt_t j=0; j<fData[0].GetDataBin(0)->size(); j++) {
      if ((j != 0) && (j % fAny2ManyInfo->rebin == 0)) {
        data[k] = ival;
        noOfEvents += ival;
        k++;
        ival = 0;
      }
      ival += static_cast<UInt_t>(fData[0].GetDataBin(i)->at(j));
    }

    // feed data relevant information
    // the numbering of the histograms start from '1', hence i+1 needed!!
    MUD_setHistType(fd, i+1, MUD_GRP_TRI_TD_HIST_ID);
    MUD_setHistNumBytes(fd, i+1, sizeof(data));
    MUD_setHistNumBins(fd, i+1, dataSize);
    MUD_setHistBytesPerBin(fd, i+1, 0);
    MUD_setHistFsPerBin(fd, i+1, static_cast<UINT32>(1.0e6*fAny2ManyInfo->rebin*fData[0].GetTimeResolution())); // time resolution is given in (ns)
    if (fData[0].GetT0Size() > i) {
      MUD_setHistT0_Ps(fd, i+1, static_cast<UINT32>(1.0e3*fData[0].GetTimeResolution()*((fData[0].GetT0(i)+fAny2ManyInfo->rebin/2)/fAny2ManyInfo->rebin)));
      MUD_setHistT0_Bin(fd, i+1, static_cast<UINT32>(fData[0].GetT0(i)/fAny2ManyInfo->rebin));
    } else {
      MUD_setHistT0_Ps(fd, i+1, 0);
      MUD_setHistT0_Bin(fd, i+1, 0);
    }
    MUD_setHistGoodBin1(fd, i+1, 0);
    MUD_setHistGoodBin2(fd, i+1, 0);
    MUD_setHistBkgd1(fd, i+1, 0);
    MUD_setHistBkgd2(fd, i+1, 0);
    MUD_setHistNumEvents(fd, i+1, (UINT32)noOfEvents);
    MUD_setHistTitle(fd, i+1, dummy);
    REAL64 timeResolution = (fAny2ManyInfo->rebin*fData[0].GetTimeResolution())/1.0e9; // ns -> s
    MUD_setHistSecondsPerBin(fd, i+1, timeResolution);

    MUD_setHistData(fd, i+1, data);
  }

  MUD_closeWrite(fd);

  delete [] data;

  // check if mud file shall be streamed to stdout
  if (fAny2ManyInfo->useStandardOutput && (fAny2ManyInfo->compressionTag == 0)) {
    // stream file to stdout
    ifstream is;
    int length=1024;
    char *buffer;

    is.open(fln.Data(), ios::binary);
    if (!is.is_open()) {
      cerr << endl << "PRunDataHandler::WriteMudFile(): **ERROR** Couldn't open the mud-file for streaming." << endl;
      remove(fln.Data());
      return false;
    }

    // get length of file
    is.seekg(0, ios::end);
    length = is.tellg();
    is.seekg(0, ios::beg);

    if (length == -1) {
      cerr << endl << "PRunDataHandler::WriteMudFile(): **ERROR** Couldn't determine the mud-file size." << endl;
      remove(fln.Data());
      return false;
    }

    // allocate memory
    buffer = new char [length];

    // read data as a block
    while (!is.eof()) {
      is.read(buffer, length);
      cout.write(buffer, length);
    }

    is.close();

    delete [] buffer;

    // delete temporary root file
    remove(fln.Data());
  }

  return true;
}

//--------------------------------------------------------------------------
// WriteAsciiFile
//--------------------------------------------------------------------------
/**
 * <p> Write the ascii-file format.
 *
 * <b>return:</b>
 * - true on successful writting,
 * - otherwise false.
 *
 * \param fln file name. If empty, the routine will try to construct one
 */
Bool_t PRunDataHandler::WriteAsciiFile(TString fln)
{
  // generate output file name
  if (fln.Length() == 0) {
    Bool_t ok = false;
    fln = GetFileName(".ascii", ok);
    if (!ok)
      return false;
  } else {
    fln.Prepend(fAny2ManyInfo->outPath);
  }
  // keep the file name if compression is whished
  fAny2ManyInfo->outPathFileName.push_back(fln);

  if (!fAny2ManyInfo->useStandardOutput)
    cout << endl << ">> PRunDataHandler::WriteAsciiFile(): writing an ascii data file (" << fln.Data() << ") ... " << endl;

  // write ascii file
  ofstream fout;
  streambuf* strm_buffer = 0;

  if (!fAny2ManyInfo->useStandardOutput || (fAny2ManyInfo->compressionTag > 0)) {
    // open data-file
    fout.open(fln.Data(), ofstream::out);
    if (!fout.is_open()) {
      cerr << endl << ">> PRunDataHandler::WriteAsciiFile **ERROR** Couldn't open data file (" << fln.Data() << ") for writing, sorry ...";
      cerr << endl;
      return false;
    }

    // save output buffer of the stream
    strm_buffer = cout.rdbuf();

    // redirect output into the file
    cout.rdbuf(fout.rdbuf());
  }

  // write header
  cout << "%*************************************************************************";
  cout << endl << "% file name   : " << fln.Data();
  if (fData[0].GetRunTitle()->Length() > 0)
    cout << endl << "% title       : " << fData[0].GetRunTitle()->Data();
  if (fData[0].GetRunNumber() >= 0)
    cout << endl << "% run number  : " << fData[0].GetRunNumber();
  if (fData[0].GetSetup()->Length() > 0)
    cout << endl << "% setup       : " << fData[0].GetSetup()->Data();
  cout << endl << "% field       : " << fData[0].GetField() << " (G)";
  if (fData[0].GetStartTime()->Length() > 0)
    cout << endl << "% date        : " << fData[0].GetStartTime()->Data() << " " << fData[0].GetStartDate()->Data() << " / " << fData[0].GetStopTime()->Data() << " " << fData[0].GetStopDate()->Data();
  if (fData[0].GetNoOfTemperatures() > 0) {
    cout << endl << "% temperature : ";
    for (UInt_t i=0; i<fData[0].GetNoOfTemperatures()-1; i++) {
      cout << fData[0].GetTemperature(i) << "+-" << fData[0].GetTempError(i) << "(K) , ";
    }
    cout << fData[0].GetTemperature(fData[0].GetNoOfTemperatures()-1) << "+-" << fData[0].GetTempError(fData[0].GetNoOfTemperatures()-1) << "(K)";
  }
  if (fData[0].GetEnergy() != PMUSR_UNDEFINED)
    cout << endl << "% energy      : " << fData[0].GetEnergy() << " (keV)";
  if (fData[0].GetTransport() != PMUSR_UNDEFINED)
    cout << endl << "% transport   : " << fData[0].GetTransport() << " (kV)";
  if (fData[0].GetTimeResolution() != PMUSR_UNDEFINED) {
    cout.precision(10);
    cout << endl << "% time resolution : " << fData[0].GetTimeResolution()*fAny2ManyInfo->rebin << " (ns)";
    cout.setf(ios::fixed,ios::floatfield);   // floatfield set to fixed
  }
  if (fData[0].GetT0Size() > 0) {
    cout << endl << "% t0          : ";
    for (UInt_t i=0; i<fData[0].GetT0Size()-1; i++) {
      cout << static_cast<UInt_t>(fData[0].GetT0(i)/fAny2ManyInfo->rebin) << ", ";
    }
    cout << fData[0].GetT0(fData[0].GetT0Size()-1)/fAny2ManyInfo->rebin;
  }
  cout << endl << "% # histos    : " << fData[0].GetNoOfHistos();
  cout << endl << "% # of bins   : " << static_cast<UInt_t>(fData[0].GetDataBin(0)->size()/fAny2ManyInfo->rebin);
  cout << endl << "%*************************************************************************";

  // write data
  if (fAny2ManyInfo->rebin == 1) {
    UInt_t length = fData[0].GetDataBin(0)->size();
    for (UInt_t i=1; i<length; i++) {
      cout << endl;
      for (UInt_t j=0; j<fData[0].GetNoOfHistos(); j++) {
        cout.width(8);
        cout << static_cast<Int_t>(fData[0].GetDataBin(j)->at(i));
      }
    }
  } else {
    PUIntVector dataRebin(fData[0].GetNoOfHistos());
    UInt_t length = fData[0].GetDataBin(0)->size();

    // initialize the dataRebin vector
    for (UInt_t i=0; i<fData[0].GetNoOfHistos(); i++)
      dataRebin[i] = static_cast<UInt_t>(fData[0].GetDataBin(i)->at(0));

    for (UInt_t i=1; i<length; i++) {
      if ((i % fAny2ManyInfo->rebin) == 0) {
        cout << endl;
        for (UInt_t j=0; j<fData[0].GetNoOfHistos(); j++) {
          cout.width(8);
          cout << dataRebin[j];
        }
        // initialize the dataRebin vector for the next pack
        for (UInt_t j=0; j<fData[0].GetNoOfHistos(); j++)
          dataRebin[j] = static_cast<UInt_t>(fData[0].GetDataBin(j)->at(i));
      } else {
        for (UInt_t j=0; j<fData[0].GetNoOfHistos(); j++)
          dataRebin[j] += static_cast<UInt_t>(fData[0].GetDataBin(j)->at(i));
      }
    }
  }

  cout << endl;

  if (!fAny2ManyInfo->useStandardOutput || (fAny2ManyInfo->compressionTag > 0)) {
    // restore old output buffer
    cout.rdbuf(strm_buffer);

    fout.close();
  }

  return true;
}


//--------------------------------------------------------------------------
// StripWhitespace (private)
//--------------------------------------------------------------------------
/**
 * <p> Strip white spaces from a string. The hope is that future TString
 * implementations of ROOT will make this routine obsolate.
 *
 * \param str string to be stripped. It will be modified directly on success.
 *
 * <b>return:</b>
 * - true at successful reading,
 * - otherwise false.
 */
Bool_t PRunDataHandler::StripWhitespace(TString &str)
{
  Char_t *s    = 0;
  Char_t *subs = 0;
  Int_t i;
  Int_t start;
  Int_t end;
  Int_t size;

  size = (Int_t)str.Length();
  s = new Char_t[size+1];

  if (!s)
    return false;

  for (Int_t i=0; i<size+1; i++)
    s[i] = str[i];
  s[size] = 0;

  // check for whitespaces at the beginning of the string
  i = 0;
  while (isblank(s[i]) || iscntrl(s[i])) {
    i++;
  }
  start = i;

  // check for whitespaces at the end of the string
  i = strlen(s);
  while (isblank(s[i]) || iscntrl(s[i])) {
    i--;
  }
  end = i;

  if (end < start)
    return false;

  // make substring
  subs = new Char_t[end-start+2];
  if (!subs)
    return false;

  strncpy(subs, s+start, end-start+1);
  subs[end-start+1] = '\0';

  str = TString(subs);

  // clean up
  if (subs) {
    delete [] subs;
    subs = 0;
  }
  if (s) {
    delete [] s;
    s = 0;
  }

  return true;
}

//--------------------------------------------------------------------------
// IsWhitespace (private)
//--------------------------------------------------------------------------
/**
 * <p> Check if a string consists only of white spaces, i.e. spaces and/or
 * ctrl-characters.
 *
 * <b>return:</b>
 * - true at successful reading,
 * - otherwise false.
 *
 * \param str string to be checked
 */
Bool_t PRunDataHandler::IsWhitespace(const Char_t *str)
{
  UInt_t i=0;

  while (isblank(str[i]) || iscntrl(str[i])) {
    if (str[i] == 0)
      break;
    i++;
  }

  if (i == strlen(str))
    return true;
  else
    return false;
}

//--------------------------------------------------------------------------
// ToDouble (private)
//--------------------------------------------------------------------------
/**
 * <p> Convert a string to a Double_t.
 *
 * <b>return:</b>
 * - returns the converted string
 * - otherwise 0.0 with ok==false
 *
 * \param str string to be converted
 * \param ok true on success, otherwise false.
 */
Double_t PRunDataHandler::ToDouble(TString &str, Bool_t &ok)
{
  Char_t *s;
  Double_t value;
  Int_t size, status;

  ok = true;

  size = (Int_t)str.Length();
  s = new Char_t[size+1];

  if (!s) {
    ok = false;
    return 0.0;
  }

  // copy string; stupid way but it works
  for (Int_t i=0; i<size+1; i++)
    s[i] = str[i];
  s[size] = 0;

  // extract value
  status = sscanf(s, "%lf", &value);
  if (status != 1) {
    ok = false;
    return 0.0;
  }

  // clean up
  if (s) {
    delete [] s;
    s = 0;
  }

  return value;
}

//--------------------------------------------------------------------------
// ToInt (private)
//--------------------------------------------------------------------------
/**
 * <p> Convert a string to an Int_t.
 *
 * <b>return:</b>
 * - returns the converted string
 * - otherwise 0.0 with ok==false
 *
 * \param str string to be converted
 * \param ok true on success, otherwise false.
 */
Int_t PRunDataHandler::ToInt(TString &str, Bool_t &ok)
{
  Char_t *s;
  Int_t value;
  Int_t size, status;

  ok = true;

  size = (Int_t)str.Length();
  s = new Char_t[size+1];

  if (!s) {
    ok = false;
    return 0;
  }

  // copy string; stupid way but it works
  for (Int_t i=0; i<size+1; i++)
    s[i] = str[i];
  s[size] = 0;

  // extract value
  status = sscanf(s, "%d", &value);
  if (status != 1) {
    ok = false;
    return 0;
  }

  // clean up
  if (s) {
    delete [] s;
    s = 0;
  }

  return value;
}

//--------------------------------------------------------------------------
// GetDataTagIndex (private)
//--------------------------------------------------------------------------
/**
 * <p> Checks if str is in a list of data tags
 *
 * <b>return:</b>
 * - if found returns the data tag index (from the dataTags vector),
 * - otherwise -1
 *
 * \param str data tag string (see description of nonMusr db-data)
 * \param dataTags vector of all data tags
 */
Int_t PRunDataHandler::GetDataTagIndex(TString &str, const PStringVector* dataTags)
{
  Int_t result = -1;

  // check all the other possible data tags
  for (UInt_t i=0; i<dataTags->size(); i++) {
    if (!dataTags->at(i).CompareTo(str, TString::kIgnoreCase)) {
      result = i;
      break;
    }
  }

  return result;
}


//--------------------------------------------------------------------------
// GetFileName (private)
//--------------------------------------------------------------------------
/**
 * <p>Construct the file name based on the any2many request.
 *
 * <b>return:</b>
 * - constructed file name
 * - empty string
 *
 * \param extension if the file name to be constructed
 * \param ok flag which is 'true' if the file name could be constructed, 'false' otherwise
 */
TString PRunDataHandler::GetFileName(const TString extension, Bool_t &ok)
{
  TString fileName = "";
  ok = true;

  Int_t start = fRunPathName.Last('/');
  Int_t end = fRunPathName.Last('.');
  if (end == -1) {
    cerr << endl << ">> PRunDataHandler::GetFileName(): **ERROR** couldn't generate the output file name ..." << endl;
    ok = false;
    return fileName;
  }
  // cut out the filename (get rid of the extension, and the path)
  Char_t str1[1024], str2[1024];
  strncpy(str1, fRunPathName.Data(), sizeof(str1));
  for (Int_t i=0; i<end-start-1; i++) {
    str2[i] = str1[i+start+1];
  }
  str2[end-start-1] = 0;

  if (fAny2ManyInfo->inFormat == fAny2ManyInfo->outFormat) { // only rebinning
    TString rebinStr;
    rebinStr += fAny2ManyInfo->rebin;
    fileName = fAny2ManyInfo->outPath + str2 + "_rebin" + rebinStr + extension;
  } else { // real conversion
    fileName = fAny2ManyInfo->outPath + str2 + extension;
  }

  return fileName;
}

//--------------------------------------------------------------------------
// FileNameFromTemplate (private)
//--------------------------------------------------------------------------
/**
 * <p>Will, based on the fileNameTemplate construct a file name. The file name template has a run tag
 * and a year tag. The run tag has the form [rrrr], whereas the number of 'r' defines the length of
 * this string part. If the run number is shorter, leading zeros will be filled, e.g. [rrrrrr] and run==123
 * will lead to '000123'. The same is true for the year tag [yy], except that the length must agree.
 * For example: fileNameTemplate = d[yyyy]/deltat_tdc_gps_[rrrr].bin, run = 123, year = 2009 will result
 * in 'd2009/deltat_tdc_gps_0123.bin'.
 *
 * <b>return:</b>
 * - constructed file name from template, run number, and year
 * - empty string
 *
 * \param template template string
 * \param run run number
 * \param year year
 * \param ok true if operation went smooth, otherwise false
 */
TString PRunDataHandler::FileNameFromTemplate(TString &fileNameTemplate, Int_t run, TString &year, Bool_t &ok)
{
  TString result("");
  TString str = fileNameTemplate;

  Int_t tag = 0;
  Int_t yearStart = -1;
  Int_t yearLength = 0;
  Int_t runStart = -1;
  Int_t runLength = 0;

  // find position and length of the year tag
  Bool_t foundLeft = false, foundRight = false;
  for (Int_t i=0; i<str.Length(); i++) {

    if (str[i] == '[') {
      foundLeft = true;
      tag = 1;
      continue;
    } else if (str[i] == ']') {
      foundRight = true;
      tag = 0;
      continue;
    }

    if (tag == 1) {
      if (str[i] == 'y') {
        if (yearStart == -1)
          yearStart = i-1;
        yearLength++;
      }
    }
  }

  if ((yearStart != -1) && (yearLength != 0)) {
    yearLength += 2; // including the []
    str.Replace(yearStart, yearLength, year);
    ok = true;
  }

  if (foundLeft && !foundRight)
    ok = false;
  else
    ok = true;

  if (ok) {
    // find position and length of the run tag
    foundLeft = false, foundRight = false;
    for (Int_t i=0; i<str.Length(); i++) {
      if (str[i] == '[') {
        foundLeft = true;
        tag = 1;
        continue;
      } else if (str[i] == ']') {
        foundRight = true;
        tag = 0;
        continue;
      }

      if (tag == 1) {
        if (str[i] == 'r') {
          if (runStart == -1)
            runStart = i-1;
          runLength++;
        }
      }
    }

    if ((runStart == -1) || (runLength == 0)) {
      cerr << endl << ">> PRunDataHandler::FileNameFromTemplate(): **ERROR** run template not ok." << endl;
      ok = false;
    } else {
      TString runStr("");
      char fmt[128];
      sprintf(fmt , "%%0%dd", runLength);
      runStr.Form(fmt, run);
      runLength += 2; // including the []
      str.Replace(runStart, runLength, runStr);
      ok = true;
    }
  }

  if (ok)
    result = str;

  return result;
}

//--------------------------------------------------------------------------
// GetMonth (private)
//--------------------------------------------------------------------------
/**
 * <p>Spits out the month as MMM for a given numerical month 0..11
 *
 * <b>return:</b>
 * - constructed file name from template, run number, and year
 * - empty string
 *
 * \param template template string
 */
TString PRunDataHandler::GetMonth(Int_t month)
{
  TString mm("");

  if (month == 0)
    mm = "JAN";
  else if (month == 1)
    mm = "FEB";
  else if (month == 2)
    mm = "MAR";
  else if (month == 3)
    mm = "APR";
  else if (month == 4)
    mm = "MAY";
  else if (month == 5)
    mm = "JUN";
  else if (month == 6)
    mm = "JUL";
  else if (month == 7)
    mm = "AUG";
  else if (month == 8)
    mm = "SEP";
  else if (month == 9)
    mm = "OCT";
  else if (month == 10)
    mm = "NOV";
  else if (month == 11)
    mm = "DEC";
  else
    mm = "???";

  return mm;
}
