Index: trunk/SophyaPI/PIext/basexecut.cc
===================================================================
--- trunk/SophyaPI/PIext/basexecut.cc	(revision 2668)
+++ trunk/SophyaPI/PIext/basexecut.cc	(revision 2669)
@@ -371,4 +371,11 @@
   if (tokens.size() < 2) { cout << "Usage: saveobjs patt filename " << endl; return(0); }
   mObjMgr->SaveObjects(tokens[0], tokens[1]);  
+}
+else if (kw == "savelist")  {
+  if (tokens.size() < 2) {
+    cout << "Usage: savelist objname1 [objname2 ...] filename "<<endl;
+    return(0);
+  }
+  mObjMgr->SaveListObjects(tokens);  
 }
 else if (kw == "saveall" ) {
@@ -1469,5 +1476,5 @@
 usage += "  PPF file (pattern: x?y*) - Alias saveppf\n";
 usage += "Usage: saveppf nameobjpattern filename";
-usage += "\n  Related commands: saveobjs saveall openppf savefits";  
+usage += "\n  Related commands: saveobjs savelist saveall openppf savefits";  
 mpiac->RegisterCommand(kw, usage, this, "FileIO");
 kw = "saveobjs";
@@ -1475,8 +1482,13 @@
 usage += "  PPF file (pattern: x?y*) - Alias saveppf\n";
 usage += "Usage: saveobjs nameobjpattern filename";
-usage += "\n  Related commands: saveppf saveall openppf savefits";  
+usage += "\n  Related commands: saveppf savelist saveall openppf savefits";  
 mpiac->RegisterCommand(kw, usage, this, "FileIO");
 kw = "saveall";
 usage = "Saves all objects into a PPF file \n Usage: saveall filename";
+usage += "\n  Related commands: saveobj savelist openppf savefits";  
+mpiac->RegisterCommand(kw, usage, this, "FileIO");
+kw = "savelist";
+usage = "Saves a list of objects into a PPF file";
+usage = "\n Usage: savelist objname1 [objname2 ...] filename";
 usage += "\n  Related commands: saveobj openppf savefits";  
 mpiac->RegisterCommand(kw, usage, this, "FileIO");
Index: trunk/SophyaPI/PIext/nobjmgr.cc
===================================================================
--- trunk/SophyaPI/PIext/nobjmgr.cc	(revision 2668)
+++ trunk/SophyaPI/PIext/nobjmgr.cc	(revision 2669)
@@ -1205,4 +1205,39 @@
 
 /* --Methode-- */
+void NamedObjMgr::SaveListObjects(vector<string> &liste)
+// Les n-1 premiers elements (liste[0 -> n-2]) contiennent
+// les noms des objects a mettre  dans le fichier ppf
+// dont le nom est le dernier argument (liste[n-1])
+{
+ ZSync(*myMutex);
+
+ if(liste.size()<2) {
+   cerr<<"NamedObjMgr::SaveListObjects()/Error not enough argument"<<endl;
+   return;
+ }
+
+ // open ppf file
+ string ppfname = liste[liste.size()-1];
+ POutPersist* pout=NULL;
+ try {
+   pout = new POutPersist(ppfname);
+ } catch(IOExc iox) {
+   cerr<<"NamedObjMgr::SaveObjects()/Error Exception - Msg= "<<iox.Msg()<<endl;
+   return;
+ }
+ 
+ // put objects in ppf file
+ for(int i=0;i<liste.size()-1;i++) {
+   bool keeppath = (liste[i][0] == '/') ? true : false;
+   SaveObj_P(liste[i], (*pout), keeppath);
+ }
+
+ // close ppf file
+ delete pout;
+
+ return;
+}
+
+/* --Methode-- */
 void NamedObjMgr::SaveAll(string const& flnm)
 {
Index: trunk/SophyaPI/PIext/nobjmgr.h
===================================================================
--- trunk/SophyaPI/PIext/nobjmgr.h	(revision 2668)
+++ trunk/SophyaPI/PIext/nobjmgr.h	(revision 2669)
@@ -75,4 +75,5 @@
   virtual void          SaveObj(string & nom, POutPersist& s, bool keeppath=false);
   virtual void          SaveObjects(string & patt, string const& nomppf);
+  virtual void          SaveListObjects(vector<string> &liste);
   virtual void          SaveAll(string const& nomppf);
   virtual void          SaveFits(string & nom, string const & flnm);
Index: trunk/SophyaPI/PIext/pawexecut.cc
===================================================================
--- trunk/SophyaPI/PIext/pawexecut.cc	(revision 2668)
+++ trunk/SophyaPI/PIext/pawexecut.cc	(revision 2669)
@@ -7,4 +7,5 @@
 #include "sopnamsp.h"
 #include "strutil.h"
+#include "strutilxx.h"
 #include "histos.h"
 #include "histos2.h"
@@ -75,4 +76,25 @@
 usage += "\n           if given add exp1,exp2,exp3 to the variable list";
 usage += "\n -f:filename : write into \"filename\", Default is to stdout";
+piac->RegisterCommand(kw,usage,this,hgrp);
+
+kw = "n/read";
+usage = "Read columns in an ASCII file and fill a NTuple";
+usage += "\n n/read nt fascii [options] var_1,c_1 var_2,c_2 ... var_n,c_n ";
+usage += "\n var_i,c_i : ntuple variable name, associated column in ASCII file [0,n[";
+usage += "\n where [options] are:";
+usage += "\n   \"=s\": separator character is \'s\' (could be \"\t\")";
+usage += "\n   \"-^abcd\": do not read lines beginning with string \"abcd\" ";
+usage += "\n   \"+^abcd\": read only lines beginning with string \"abcd\" ";
+usage += "\n   \"-abcd\": do not read lines which contain string \"abcd\" ";
+usage += "\n   \"+abcd\": read only lines which contain string \"abcd\" ";
+usage += "\n these options may be repeated (ex: \"-^abcd\" \"-^xyz\") ";
+usage += "\n   - in case of \"do not read\" options are added with logical AND ";
+usage += "\n   - in case of \"read only\" options are added with logical OR ";
+piac->RegisterCommand(kw,usage,this,hgrp);
+
+kw = "n/merge";
+usage = "Merge ntuples";
+usage += "\n n/merge nt nt_1 nt_2 ... nt_n";
+usage += "\n Merge ntuples nt_i into ntuple nt";
 piac->RegisterCommand(kw,usage,this,hgrp);
 
@@ -235,4 +257,8 @@
 } else if(kw == "n/scan") {
   n_scan(tokens); return(0);
+} else if(kw == "n/read") {
+  n_read(tokens); return(0);
+} else if(kw == "n/merge") {
+  n_merge(tokens); return(0);
 } else if(kw == "h/integ") {
   h_integ(tokens); return(0);
@@ -561,4 +587,275 @@
 
 /* methode */
+#define __LENLINE_N_READ__ 8192
+void PAWExecutor::n_read(vector<string>& tokens)
+{
+ int lp=1;
+
+ if(tokens.size()<3) {
+   cerr<<"Usage: n/read nt fascii [options] var_1,c_1 ... var_n,c_n"<<endl;
+   return;
+ }
+
+ // decodage des arguments
+ string nament = tokens[0];
+ string nameascii = tokens[1];
+ vector<string> donotreadbeg;
+ vector<string> donotreadin;
+ vector<string> onlyreadbeg;
+ vector<string> onlyreadin;
+ vector<string> varname;
+ vector<int> colnum;
+ char separator = ' ';
+ int numcolmaxi=-1;
+
+ for(int i=2;i<tokens.size();i++) {
+   int lc = tokens[i].size();
+   if(lc<2) continue;
+   const char *c = tokens[i].c_str();
+   if(c[0]=='=') { // Separator
+     separator = c[1];
+     if(lc==3) if(c[1]=='\\' && c[2]=='t') separator = '\t';
+     continue;
+   }
+   if(c[0]=='+') { // Selection des lignes a lire
+     if(c[1]!='^') onlyreadin.push_back(&c[1]);
+       else if(lc>2) onlyreadbeg.push_back(&c[2]);
+     continue;
+   }
+   if(c[0]=='-') { // Selection des lignes commentaire
+     if(c[1]!='^') donotreadin.push_back(&c[1]);
+       else if(lc>2) donotreadbeg.push_back(&c[2]);
+     continue;
+   }
+   // decodage des noms de variables et des colonnes associees
+   int p = tokens[i].find(',');
+   if(p<1 || p>=lc-1) continue;
+   string vn = tokens[i].substr(0,p);
+   string cn = tokens[i].substr(p+1,lc-p-1);
+   int ic = atoi(cn.c_str());
+   if(ic<0) continue;
+   if( !isalpha(vn[0]) ) continue;
+   if(ic>numcolmaxi) numcolmaxi = ic;
+   varname.push_back(vn);
+   colnum.push_back(ic);
+ }
+
+ int nvar = varname.size();
+ if(nvar<=0) {
+   cerr<<"n_read: no variables to be read"<<endl;
+   return;
+ }
+
+ // Print what has to be done
+ if(lp) {
+   if(onlyreadin.size()>0) {
+     cout<<"n_read Only read line containing ["<<onlyreadin.size()<<"]:";
+     for(int i=0;i<onlyreadin.size();i++) cout<<" \'"<<onlyreadin[i]<<"\'";
+     cout<<endl;
+   }
+   if(onlyreadbeg.size()>0) {
+     cout<<"n_read Only read line begining with ["<<onlyreadbeg.size()<<"]:";
+     for(int i=0;i<onlyreadbeg.size();i++) cout<<" \'"<<onlyreadbeg[i]<<"\'";
+     cout<<endl;
+   }
+   if(donotreadin.size()>0) {
+     cout<<"n_read Do not read line containing ["<<donotreadin.size()<<"]:";
+     for(int i=0;i<donotreadin.size();i++) cout<<" \'"<<donotreadin[i]<<"\'";
+     cout<<endl;
+   }
+   if(donotreadbeg.size()>0) {
+     cout<<"n_read Do not read line begining with ["<<donotreadbeg.size()<<"]:";
+     for(int i=0;i<donotreadbeg.size();i++) cout<<" \'"<<donotreadbeg[i]<<"\'";
+     cout<<endl;
+   }
+   if(nvar>0) {
+     cout<<"n_read Number of variables to be read: "<<nvar<<endl;
+     for(int i=0;i<nvar;i++) cout<<" \'"<<varname[i].c_str()<<","<<colnum[i]<<"\'";
+     cout<<endl;
+   }
+   if(separator!=' ') cout<<"n_read Separator is: \'"<<separator<<"\'"<<endl;
+ }
+
+ // Open ASCII file
+ FILE * fascii = fopen(nameascii.c_str(),"r");
+ if(fascii==NULL) {
+   cerr<<"n_read: cannot open file "<<nameascii<<endl;
+   return;
+ }
+
+ // Creation du NTuple
+ char** ntvn = new char*[nvar];
+ for(int i=0;i<nvar;i++) ntvn[i] = const_cast<char *>(varname[i].c_str());
+ r_4 *xnt = new r_4[nvar];
+ NTuple *nt = new NTuple(nvar,ntvn);
+
+ // Read file
+ char *line = new char[__LENLINE_N_READ__];
+ int nline=0, nlinecom=0;
+ while(fgets(line,__LENLINE_N_READ__,fascii) != NULL ) {
+   nline++;
+   int lc = strlen(line); if(lc<1) continue;
+   // Pour enlever le \n final
+   if(line[lc-1]=='\n' || line[lc-1]=='\r')
+     {line[lc-1]='\0'; lc = strlen(line); if(lc<1) continue;}
+
+   // On vire les blancs au debut et a la fin
+   char *newline = line;
+   for(int i=0;i<lc;i++) if(newline[i]!=' ') {newline = &line[i]; break;}
+   lc = strlen(newline); if(lc<1) continue;
+   for(int i=lc-1;i>=0;i--) if(newline[i]==' ') newline[i]='\0'; else break;
+   lc = strlen(newline); if(lc<1) continue;
+   string const sline(newline);
+   //cout<<"\'"<<sline<<"\'  lc="<<lc<<endl;
+
+   // Faut t'il lire cette ligne ?
+   bool read_line_1 = true;
+   if(onlyreadin.size()>0 || onlyreadbeg.size()>0) read_line_1 = false;
+   if(onlyreadin.size()>0) {
+     for(int i=0;i<onlyreadin.size();i++) {
+       uint_4 p = sline.find(onlyreadin[i].c_str());
+       if(p<0 || p>=lc) continue;
+       read_line_1 = true;
+       break;
+     }
+   }
+   if(onlyreadbeg.size()>0) {
+     for(int i=0;i<onlyreadbeg.size();i++) {
+       uint_4 p = sline.find(onlyreadbeg[i].c_str());
+       if(p!=0) continue;
+       read_line_1 = true;
+       break;
+     }
+   }
+
+   // Faut t'il ne pas lire cette ligne ?
+   bool read_line_2 = true;
+   if(donotreadin.size()>0) {
+     for(int i=0;i<donotreadin.size();i++) {
+       uint_4 p = sline.find(donotreadin[i].c_str());
+       if(p<0 || p>=lc) continue;
+       read_line_2 = false;
+       break;
+     }
+   }
+   if(donotreadbeg.size()>0) {
+     for(int i=0;i<donotreadbeg.size();i++) {
+       uint_4 p = sline.find(donotreadbeg[i].c_str());
+       if(p!=0) continue;
+       read_line_2 = false;
+       break;
+     }
+   }
+   if(!read_line_2) nlinecom++;
+
+   if(!read_line_1 || !read_line_2) continue;
+
+   // Decodage de la ligne
+   vector<string> vs;
+   FillVStringFrString(sline,vs,separator);
+   int lvs = vs.size();
+   if(lvs<numcolmaxi) continue; // Pas assez de champs decodes, mauvaise ligne
+
+   // Remplissage du NTuple
+   for(int i=0;i<nvar;i++) {
+     xnt[i] = 0.;
+     int ic = colnum[i];
+     if(ic>=lvs) continue;
+     xnt[i] = atof(vs[ic].c_str());
+   }
+   nt->Fill(xnt);
+   //cout<<"...xnt"; for(int i=0;i<nvar;i++) cout<<" "<<xnt[i]; cout<<endl;
+
+ }
+ cout<<"n_read: "<<nline<<" lines in file, "
+     <<nlinecom<<" commentary, "
+     <<nt->NEntry()<<" Ntuple entries"<<endl;
+
+ // On sauve le NTuple si besoin, on ferme et detruit ce qu'il faut
+ NamedObjMgr omg;
+ if(nt->NEntry()>0) omg.AddObj(nt,nament); else delete nt;
+ delete [] ntvn;
+ delete [] xnt;
+ delete [] line;
+ fclose(fascii);
+
+ return;
+}
+#undef __LENLINE_N_READ__
+
+/* methode */
+void PAWExecutor::n_merge(vector<string>& tokens)
+{
+ if(tokens.size()<2) {
+   cerr<<"Usage: n/read nt nt_1 nt_2 ... nt_n"<<endl;
+   return;
+ }
+
+ NamedObjMgr omg;
+
+ // decodage des arguments
+ string nament = tokens[0];
+
+ // boucle sur les ntuples
+ NTuple * nt = NULL;
+ int nvar=0, nfill=0;
+ r_4 *xnt=NULL;
+
+ for(int i=1;i<tokens.size();i++) {
+
+   AnyDataObj* mobj = omg.GetObj(tokens[i]);
+   if(mobj==NULL) {
+     cout<<"n_merge Error: unknow object"<<tokens[i]<<endl;
+     continue;;
+   }
+   NTuple* nt1 = dynamic_cast<NTuple*>(mobj);
+   if(nt1==NULL) {
+     cout<<"n_merge Error: "<<tokens[i]<<" not a NTuple"<<endl;
+     continue;
+   }
+   if(nt1->NEntry()==0) {
+     cout<<"n_merge Error: "<<tokens[i]<<" is empty"<<endl;
+     continue;
+   }
+   if(nt1->NVar()==0) {
+     cout<<"n_merge Error: "<<tokens[i]<<" has no variable"<<endl;
+     continue;
+   }
+
+   // create receiving ntuple if first pass
+   if(nt==NULL) {
+     nvar = nt1->NVar();
+     vector<string> sntvn;
+     for(int i=0;i<nvar;i++) sntvn.push_back(nt1->ColumnName(i));
+     char **ntvn = new char*[nvar];
+     for(int i=0;i<nvar;i++) ntvn[i] = const_cast<char *>(sntvn[i].c_str());
+     nt = new NTuple(nvar,ntvn);
+     delete [] ntvn;
+   }
+
+   // filling with current ntuple
+   int nvar1 = nt1->NVar();
+   int n = (nvar1>nvar)? nvar1: nvar;
+   r_4 *xnt1 = new r_4[n];
+   for(int i=0;i<n;i++) xnt1[i]=0.;
+   for(uint_4 iev=0;iev<nt1->NEntry();iev++) {
+     nt1->GetVec(iev,xnt1);
+     nt->Fill(xnt1);
+   }
+   nfill++;
+   delete [] xnt1;
+
+ }
+
+ if(xnt!=NULL) delete [] xnt;
+ if(nt!=NULL) {
+   cout<<"n_merge: ntuple filled with "<<nfill
+       <<" ntuples, "<<nt->NEntry()<<" entries"<<endl;
+   if(nt->NEntry()>0) omg.AddObj(nt,nament); else delete nt;
+ }
+ return;
+}
+
+/* methode */
 void PAWExecutor::h_integ(vector<string>& tokens)
 // Pour remplacer le contenu d'un histo 1D par son integrale
Index: trunk/SophyaPI/PIext/pawexecut.h
===================================================================
--- trunk/SophyaPI/PIext/pawexecut.h	(revision 2668)
+++ trunk/SophyaPI/PIext/pawexecut.h	(revision 2669)
@@ -24,4 +24,6 @@
   void  n_proj(vector<string>& tokens);
   void  n_scan(vector<string>& tokens);
+  void  n_read(vector<string>& tokens);
+  void  n_merge(vector<string>& tokens);
   void  h_integ(vector<string>& tokens);
   void  v_integ(vector<string>& tokens);
