/*-*- c++ -*-******************************************************************
* GraceTMPL Library
* Copyright (C) 2001,2002 Andy Thaller
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*****************************************************************************/
#include "gracetmpl.h"
#include <vector>
#include <map>
#include <iostream>
extern "C" {
#include <math.h>
#include <float.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
}
using namespace GraceTMPL;
using namespace std;
/* ********************************************************************
*
* Global methods in namespace GraceTMPL
*
* *******************************************************************/
string GraceTMPL::smashVars(const string &from)
{
string to(from), param, cont;
typedef string::size_type ST;
ST p1=0,p2;
while ((p1=to.find("$",p1))!=string::npos) {
if (to[p1+1]=='{') { // this is ${PARAMNAME}
if ((p2= to.find("}",p1+1))==string::npos) {
fprintf(stderr,"no closing bracket in template <%s>\n",to.c_str());
return to;
}
param= to.substr(p1+2,p2-p1-2);
ST pc=param.find("::",0);
if (pc!=string::npos) {
cont= param.substr(0,pc);
param= param.substr(pc+2,param.length());
}
} else { // this is $PARAMNAME
p2= p1+1;
while (isalnum(to[p2]) || to[p2]=='_') ++p2;
--p2;
param= to.substr(p1+1,p2-p1);
}
// smash the $NAME or ${CONTEXT::NAME} occurence:
to.replace(p1,p2-p1+1,"");
}
return to;
}
string GraceTMPL::stringNum(double d, const char *fmt)
{
char tmp[1000];
snprintf(tmp,1000,fmt,d);
return string(tmp);
}
string GraceTMPL::stringNum(float d, const char *fmt)
{
char tmp[1000];
snprintf(tmp,1000,fmt,d);
return string(tmp);
}
string GraceTMPL::stringNum(long i, const char *fmt)
{
char tmp[1000];
snprintf(tmp,1000,fmt,i);
return string(tmp);
}
string GraceTMPL::stringNum(int i, const char *fmt)
{
char tmp[1000];
snprintf(tmp,1000,fmt,i);
return string(tmp);
}
/* ********************************************************************
*
* some supplementary methods not exported by GraceTMPL
*
* *******************************************************************/
void appendStringVec(StringVec *a,
StringVec *b)
{
if (!a||!b) return;
if (a==b) return;
StringVec::iterator daStr;
for (daStr= b->begin(); daStr!= b->end(); ++daStr) {
a->push_back(*daStr);
}
}
string findString(StringVec *a, const char *first)
{
if (!a||!first) return string("");
StringVec::iterator daStr;
for (daStr= a->begin(); daStr!= a->end(); ++daStr) {
// fprintf(stderr,"checking <%s> against <%s>\n",daStr->c_str(),first);
if (!strncmp(daStr->c_str(),first,strlen(first)))
return *daStr;
}
return string("");
}
int replaceString(StringVec *a, const char *first, const string &next)
{
if (!a||!first) return 0;
StringVec::iterator daStr;
for (daStr= a->begin(); daStr!= a->end(); ++daStr) {
// fprintf(stderr,"checking <%s> against <%s>\n",daStr->c_str(),first);
if (!strncmp(daStr->c_str(),first,strlen(first))) {
*daStr= next;
return 1;
}
}
return 0;
}
int getScalar(StringVec *a, const char *first, double &x)
{
if (!a || !first) return 0;
StringVec::iterator daStr;
for (daStr= a->begin(); daStr!= a->end(); ++daStr) {
if (!strncmp(daStr->c_str(),first,strlen(first)))
if (sscanf(daStr->c_str()+strlen(first),"%lg",&x)==1)
return 1;
}
return 0;
}
int getLoc(StringVec *a, const char *first, double &x, double &y)
{
if (!a || !first) return 0;
StringVec::iterator daStr;
for (daStr= a->begin(); daStr!= a->end(); ++daStr) {
if (!strncmp(daStr->c_str(),first,strlen(first)))
if (sscanf(daStr->c_str()+strlen(first),"%lg, %lg",&x,&y)==2)
return 1;
}
return 0;
}
int replaceLoc(StringVec *a, const char *first, double x, double y)
{
double tx, ty;
if (!a || !first) return 0;
StringVec::iterator daStr;
for (daStr= a->begin(); daStr!= a->end(); ++daStr) {
if (!strncmp(daStr->c_str(),first,strlen(first)))
if (sscanf(daStr->c_str()+strlen(first),"%lg, %lg",&tx,&ty)==2) {
// fprintf(stderr,"replaciong loc <%s> with",daStr->c_str());
*daStr= string(first)+stringNum(x)+string(", ")+stringNum(y);
// fprintf(stderr," <%s>\n",daStr->c_str());
return 1;
}
}
return 0;
}
string Environment::expand(const string &from, int nests)
{
string to(from), param, cont, fallback, replacement;
typedef string::size_type ST;
ST p1=0,p2;
if ((p1=to.find("$",p1))==string::npos) return from;
while ((p1=to.find("$",p1))!=string::npos) {
if (to[p1+1]=='{') { // this is ${PARAMNAME}
if ((p2= to.find("}",p1+1))==string::npos) {
fprintf(stderr,"no closing bracket in template <%s>\n",to.c_str());
return to;
}
fallback=string("${")+(param= to.substr(p1+2,p2-p1-2))+"}";
if (param.find("$",0)!=string::npos) { // nested params...
p1++; continue;
}
// search from right to left to find whole context!
ST pc=param.rfind("::",param.length()-1);
if (pc!=string::npos) {
cont= param.substr(0,pc);
if (cont=="") cont= "std";
param= param.substr(pc+2,param.length());
}
pc=param.find(":",0);
if (pc!=string::npos) {
fallback= param.substr(pc+1,param.length());
param= param.substr(0,pc);
}
} else { // this is $PARAMNAME
p2= p1+1;
while (isalnum(to[p2]) || to[p2]=='_') ++p2;
--p2;
param= to.substr(p1+1,p2-p1);
fallback= string("$")+param;
}
replacement= substitute(cont,param,fallback);
to.replace(p1,p2-p1+1,replacement);
p1+= replacement.size();
}
// cerr << "status("<<name_<<"): "<<from<<" -> "<<to<<"\n";
if (to == from || nests <=0) return to;
return expand(to,--nests);
}
string Environment::substitute(const string &context,
const string &variable,
const string &fallback)
{
/*
int mydebug=0;
if (mydebug)
cerr <<"** \""<<name_<<"\" entering"
<< " with (\""<<context<<"\",\""<<variable<<"\",\""<<fallback<<"\")\n";
*/
if ((context=="" || context==name_) ||
(!parent_ && context=="std")) {
//if (mydebug) cerr <<"** \""<<name_<< "\" my task!\n";
StringMap::iterator daString= variable_.find(variable);
if (daString!=variable_.end()) {
//if (mydebug) cerr << "** returning my variable\n";
return daString->second;
}
}
if (context!="") {
string tcontext(context), ncontext;
string::size_type tc=tcontext.find("::",0);
if (tc!=string::npos) {
ncontext= tcontext.substr(tc+2,tcontext.length());
tcontext= tcontext.substr(0,tc);
}
/*
if (mydebug)
cerr << "** \""<<name_<< "\" contexts = "<<tcontext<<","<<ncontext<<"\n";
*/
std::map<std::string,Environment *>::iterator daEnv= context_.find(tcontext);
if (daEnv!=context_.end() && daEnv->second) {
return daEnv->second->substitute(ncontext,variable,fallback);
}
/*else {
if (mydebug) cerr << "** \""<<name_<< "\" found no "<<tcontext<<"\n";
}*/
}
if (parent_) {
//if (mydebug) cerr << "** \""<<name_<< "\" returning what parent says\n";
return parent_->substitute(context,variable,fallback);
}
//if (mydebug) cerr << "** \""<<name_<< "\" returning fallback\n";
return fallback;
}
#define DUMMY_NAME "<*=-dummy-=*>"
Data::Data() :
EnvironmentUser(),
name_(DUMMY_NAME),
n_(0),
setnum_(0),
x_(0),
y_(0),
dx_(0),
dy_(0),
xoffs_(0.0),
yoffs_(0.0),
scale_(1.0)
{
}
Data::Data(const string &name, int n,
const double *x, const double *y,
const double *dx, const double *dy) :
EnvironmentUser(),
name_(name),
n_(n),
setnum_(0),
x_(0),
y_(0),
dx_(0),
dy_(0),
xoffs_(0.0),
yoffs_(0.0),
scale_(1.0)
{
if (n) {
if (x) {
x_= new double[n];
memcpy(x_,x,sizeof(double)*n);
}
if (y) {
y_= new double[n];
memcpy(y_,y,sizeof(double)*n);
}
if (dx) {
dx_= new double[n];
memcpy(dx_,dx,sizeof(double)*n);
}
if (dy) {
dy_= new double[n];
memcpy(dy_,dy,sizeof(double)*n);
}
}
}
#if 0
void Data::autoscale(double &xmin, double &xmax,
double &ymin, double &ymax,
double errorfac)
{
if (x_) for (int i= 0; i<n_; i++) {
double xerr = (dx_) ? dx_[i] * errorfac : 0.0;
if (xmin>(x_[i]-xoffs_-fabs(xerr))) xmin= (x_[i]-xoffs_-fabs(xerr));
if (xmax<(x_[i]-xoffs_+fabs(xerr))) xmax= (x_[i]-xoffs_+fabs(xerr));
}
if (y_) for (int i= 0; i<n_; i++) {
double yerr = (dy_) ? dy_[i] * errorfac : 0.0;
if (ymin>(y_[i]-yoffs_-fabs(yerr))*scale_)
ymin= (y_[i]-yoffs_-fabs(yerr))*scale_;
if (ymax<(y_[i]-yoffs_+fabs(yerr))*scale_)
ymax= (y_[i]-yoffs_+fabs(yerr))*scale_;
}
}
#endif
void Data::autoscale(double &xmin, double &xmax,
double &ymin, double &ymax,
double XMIN, double XMAX,
double YMIN, double YMAX,
double errorfac)
{
if (x_ && y_) for (int i= 0; i<n_; i++) {
double xerr = (dx_) ? fabs(dx_[i] * errorfac) : 0.0;
double yerr = (dy_) ? fabs(dy_[i] * errorfac) : 0.0;
double xl=x_[i]-xoffs_-xerr;
double xh=x_[i]-xoffs_+xerr;
double yl=(y_[i]-yoffs_-4*yerr)*scale_;
double yh=(y_[i]-yoffs_+4*yerr)*scale_;
if (xh<XMIN || xl>XMAX || yh<YMIN || yl>YMAX) {
continue;
}
if (xmin>xl) xmin= xl;
if (xmax<xh) xmax= xh;
if (ymin>yl) ymin= yl;
if (ymax<yh) ymax= yh;
} else {
cerr << "GraceTMPL::Data::autoscale() internal error - no data!\n";
}
}
void Data::saveinfo(FILE *f, const StringVec *daSet0)
{
if (!daSet0) return;
// under any circumstances avoid altering daSet0
StringVec mySet(*daSet0);
StringVec *daSet= &mySet;
string legend= findString(daSet,"legend \"");
if (!legend.length()) legend= findString(daSet,"legend \"");
if (legend.find("$'\"",0)!=string::npos) { // literal legend string
//new copydata fprintf(stderr,"oh yes(%d), literal legend! %s\n",setnum_,legend.c_str());
legend.replace(legend.find("$'"),2,string(""));
legend= expand(legend);
replaceString(daSet,"legend \"",legend);
replaceString(daSet,"legend \"",legend);
} else if (legend.find("$!\"",0)!=string::npos) { // literal data string
legend.replace(legend.find("$!"),2,string(""));
legend= expand(legend);
replaceString(daSet,"legend \"",legend);
replaceString(daSet,"legend \"",legend);
} else { // no literal legend string
legend= expand(name_);
if (!replaceString(daSet,"legend \"",
string("legend \"")+legend+string("\"")) &&
!replaceString(daSet,"legend \"",
string("legend \"")+legend+string("\"")))
daSet->push_back(string("legend \"")+legend+string("\""));
}
StringVec::iterator daStr;
for (daStr= daSet->begin(); daStr!= daSet->end(); ++daStr) {
fprintf(f,"@ s%d %s\n",setnum_,daStr->c_str());
}
}
void Data::savedata(FILE *f, int correctLog)
{
if (!x_ || !y_) return;
fprintf(f,"@type xy%s%s\n",(dx_)?"dx":"",(dy_)?"dy":"");
if (!correctLog) {
for (int hi=0; hi<n_; ++hi) {
fprintf(f,"%16.8g %16.8g",x_[hi]-xoffs_,(y_[hi]-yoffs_)*scale_);
if (dx_) fprintf(f," %16.8g",dx_[hi]);
if (dy_) fprintf(f," %16.8g",dy_[hi]*scale_);
fprintf(f,"\n");
}
} else {
for (int hi=0; hi<n_; ++hi) {
if (dy_) {
if ((y_[hi]-yoffs_-dy_[hi])*scale_ > 0) {
fprintf(f,"%e\t%e",x_[hi]-xoffs_, (y_[hi]-yoffs_)*scale_);
if (dx_) fprintf(f,"\t%e",dx_[hi]);
fprintf(f,"\t%e",dy_[hi]*scale_);
fprintf(f,"\n");
}
} else {
if ((y_[hi]-yoffs_)*scale_ > 0) {
fprintf(f,"%e\t%e",x_[hi]-xoffs_,(y_[hi]-yoffs_)*scale_);
if (dx_) fprintf(f,"\t%e",dx_[hi]);
fprintf(f,"\n");
}
}
}
}
fprintf(f,"&\n");
}
/************************************************************************/
/************************************************************************/
/************************************************************************/
/************************************************************************/
/************************************************************************/
Graph::Graph(Save *saver, int logplot) :
EnvironmentUser(),
graphnum_(0),
saver_(saver),
xoffs_(0.0),
yoffs_(0.0),
scale_(1.0),
correctLog_(logplot)
{
}
void Graph::addParam(const string &name,double value)
{
params_.push_back(name+string(" = ")+stringNum(value));
}
int isNoscale(StringVec *a,const string &which)
{
string label= findString(a, string(which+string(" label")).c_str());
if (!label.length())
label= findString(a, string(which+string(" label")).c_str());
typedef string::size_type ST;
ST p1,p2;
p1=label.find("\"",0); if (p1==string::npos) return 0;
p2=label.find("\"",p1+1); if (p2==string::npos) return 0;
if ( /*p2-p1>5 && */ // we allow " " to be valid, too
label[p1+1]==' ' && label[p1+2]==' ' /*&& label[p1+3]!=' '*/ &&
label[p2-1]==' ' && label[p2-2]==' ' /*&& label[p2-3]!=' '*/)
return 1;
return 0;
}
void Graph::saveprep(const StringVecMap *daSets0)
{
StringVecMap mySets(*daSets0);
StringVecMap *daSets= &mySets;
for (int i=0; i<int(daSets->size()); i++) { // better than "<1000" !!
string legend= findString(&((*daSets)[i]),"legend \"");
if (!legend.length()) legend= findString(&((*daSets)[i]),"legend \"");
/* new copydata
fprintf(stderr,"blabla %d %d (%d) (%s) %d\n",graphnum_,i,dataVec_.size(),
legend.c_str(),saver_->isCopydata(graphnum_,i));
*/
if (legend.find("$!\"",0)!=string::npos) {
/* if this method is called more than once, avoid inserting dummies
* all over again
*/
if (i<int(dataVec_.size())) {
if (dataVec_[i]->name()!=string(DUMMY_NAME)) {
vector<Data *>::iterator insPos(&(dataVec_[i]));
dataVec_.insert(insPos,new Data());
}
//new copydata fprintf(stderr," inserting literal data\n");
} else {
dataVec_.push_back(new Data());
//new copydata fprintf(stderr," appending literal data\n");
}
// set this graph's environment as parent for a dummy env in dummy set
dataVec_[i]->setenv(new Environment(env()));
literalData_[i]= 1;
} else if (saver_->isCopydata(graphnum_,i)) {
/* if this method is called more than once, avoid inserting dummies
* all over again
*/
if (i<int(dataVec_.size())) {
if (dataVec_[i]->name()!=string(DUMMY_NAME)) {
vector<Data *>::iterator insPos(&(dataVec_[i]));
dataVec_.insert(insPos,new Data());
}
//new copydata fprintf(stderr," inserting copy data\n");
} else {
dataVec_.push_back(new Data());
//new copydata fprintf(stderr," appending copy data\n");
}
// set this graph's environment as parent for a dummy env in dummy set
dataVec_[i]->setenv(new Environment(env()));
literalData_[i]= 2;
}
if (i<int(dataVec_.size()))
saver_->regCopydata(graphnum_,i,dataVec_[i]);
}
std::vector<Data *>::iterator daData;
int snum= 0;
for (daData=dataVec_.begin(); daData!=dataVec_.end(); ++daData, ++snum) {
Data *d= *daData;
d->setNum(snum); // is set in dummy sets for literal or copy data!!
d->env()->setName(string("s")+stringNum(snum));
}
}
void Graph::saveinfo(FILE *f,
const StringVec *daGraph0,
const StringVecMap *daSets0,
const StringVecMap *daStrings)
{
StringVec myGraph(*daGraph0);
StringVec *daGraph= &myGraph;
StringVecMap mySets(*daSets0);
StringVecMap *daSets= &mySets;
std::vector<Data *>::iterator daData;
double xmin= DBL_MAX, xmax=-DBL_MAX, ymin= DBL_MAX, ymax=-DBL_MAX;
double XMIN=-DBL_MAX, XMAX= DBL_MAX, YMIN=-DBL_MAX, YMAX= DBL_MAX;
if (isNoscale(daGraph,"xaxis")) {
double lower,upper;
if (getScalar(daGraph,"world xmin ",lower) &&
getScalar(daGraph,"world xmax ",upper)) {
//cerr << "no autoscale on xaxis ("<<lower<<":"<<upper<<")\n";
XMIN=lower;
XMAX=upper;
}
}
if (isNoscale(daGraph,"yaxis")) {
double lower,upper;
if (getScalar(daGraph,"world ymin ",lower) &&
getScalar(daGraph,"world ymax ",upper)) {
//cerr << "no autoscale on yaxis ("<<lower<<":"<<upper<<")\n";
YMIN=lower;
YMAX=upper;
}
}
int snum= 0;
for (daData=dataVec_.begin(); daData!=dataVec_.end(); ++daData, ++snum) {
/* avoid autoscaling hidden data sets: */
StringVec *daSet= &((*daSets)[snum]);
string state= findString(daSet,"hidden ");
if (state=="hidden true") continue;
if (saver_->isCopydata(graphnum_,snum) &&
saver_->copydata(graphnum_,snum) &&
saver_->copydata(graphnum_,snum)->name()!=DUMMY_NAME) {
// the name check avoids copying from dummy sets!
/* new copydata
fprintf(stderr,"wow, %d/%d must be copied from %x\n",graphnum_,snum,
saver_->copydata(graphnum_,snum));
*/
dataVec_[snum]= saver_->copydata(graphnum_,snum);
dataVec_[snum]->autoscale(xmin,xmax,ymin,ymax,XMIN,XMAX,YMIN,YMAX,0.3);
} else if (literalData_[snum]) {
//new copydata fprintf(stderr,"lit, %d/%d...\n",graphnum_,snum);
} else {
//new copydata fprintf(stderr,"hmm, %d/%d...\n",graphnum_,snum);
Data *d= *daData;
d->setXOffset(xoffset());
d->setYOffset(yoffset());
d->setScaling(scale());
d->autoscale(xmin,xmax,ymin,ymax,XMIN,XMAX,YMIN,YMAX,0.3);
//cerr << "scaled "<<snum<<" to "<<xmin<<":"<<xmax<<" "<<ymin<<":"<<ymax<<"\n";
}
}
if (daGraph) {
if (xmin==xmax) { xmin= xmin-1.0; xmax= xmax+1.0; }
if (ymin==ymax) { ymin= ymin-1.0; ymax= ymax+1.0; }
double dx= (xmax-xmin)/5; if (dx<0.0) dx= 1.0;
double dy= (ymax-ymin)/5; if (dy<0.0) dy= 1.0;
int xmag= int(floor(log10(dx)));
int ymag= int(floor(log10(dy)));
dx= ceil(dx/pow(10.0,xmag))*pow(10.0,xmag);
dy= ceil(dy/pow(10.0,ymag))*pow(10.0,ymag);
if (!isNoscale(daGraph,"xaxis") && xmin!=DBL_MAX){
replaceString(daGraph,"world xmin ",
string ("world xmin ")+stringNum(xmin));
replaceString(daGraph,"world xmax ",
string ("world xmax ")+stringNum(xmax));
replaceString(daGraph,"xaxis tick major ",
string ("xaxis tick major ")+stringNum(dx));
replaceString(daGraph,"xaxis tick major ",
string ("xaxis tick major ")+stringNum(dx));
}
if (!isNoscale(daGraph,"yaxis") && ymin!=DBL_MAX){
replaceString(daGraph,"world ymin ",
string ("world ymin ")+stringNum(ymin));
replaceString(daGraph,"world ymax ",
string ("world ymax ")+stringNum(ymax));
replaceString(daGraph,"yaxis tick major ",
string ("yaxis tick major ")+stringNum(dy));
replaceString(daGraph,"yaxis tick major ",
string ("yaxis tick major ")+stringNum(dy));
}
// Work on automatically expanding string fields:
StringVec::iterator daStr;
if (daStrings && daStrings->size()) {
StringVecMap myStrings= *daStrings;
int str= 0;
double x0= 0, y0= 0, x= 0, y= 0, dx= 0.0, dy= 0.0;
if ((myStrings)[0].size()) {
if (!getLoc(&(myStrings)[0],"string ",x0,y0)) {
fprintf(stderr,"didn't find location of string 0\n");
} else {
for (daStr= params_.begin(); daStr!= params_.end();
++daStr, ++str) {
StringVec strTmpl= (myStrings)[str];
if (strTmpl.begin()==strTmpl.end() ||
!getLoc(&strTmpl,"string ",x,y)) {
(myStrings)[str]= (myStrings)[str-1];
strTmpl= (myStrings)[str];
x= x0+dx;
y= y0+dy;
replaceLoc(&strTmpl,"string ",x,y);
} else {
getLoc(&strTmpl,"string ",x,y);
}
replaceString(&strTmpl,"string def \"",
string ("string def \"")+(*daStr)+string("\""));
fprintf(f,"@with string\n");
StringVec::iterator da2Str;
for (da2Str= strTmpl.begin(); da2Str!= strTmpl.end(); ++da2Str) {
fprintf(f,"@ %s\n",da2Str->c_str());
}
dx= x-x0;
dy= y-y0;
x0= x;
y0= y;
}
}
}
} // if (daStrings && daStrings->size())
// if we have NO definitions in daGraph, don't even use '@with ...'
if (daGraph->begin()!=daGraph->end()) {
fprintf(f,"@with g%d\n",graphnum_);
for (daStr= daGraph->begin(); daStr!= daGraph->end(); ++daStr) {
fprintf(f,"@ %s\n",expand(*daStr).c_str());
}
}
}
snum= 0;
for (daData=dataVec_.begin(); daData!=dataVec_.end(); ++daData, ++snum) {
Data *d= *daData;
// The next line is a dirty hack to make copy data work
// It does no harm however since the original setnum will not be needed
// afterwards or can be set again.
d->setNum(snum);
if (daSets)
d->saveinfo(f,&((*daSets)[d->num()]));
else
d->saveinfo(f,0);
}
}
void Graph::savedata(FILE *f, StringMap *literalData, int dataonly)
{
std::vector<Data *>::iterator daData;
for (daData=dataVec_.begin(); daData!=dataVec_.end(); ++daData) {
Data *d= *daData;
if (d->name()==string(DUMMY_NAME)) {
if (!dataonly)
fprintf(f,"@target G%d.S%d\n",graphnum_,d->num());
string name(string("G")+stringNum(graphnum_)+
string(".S")+stringNum(d->num()));
// fprintf(stderr,"%s is literal data\n",name.c_str());
fprintf(f,"%s",(*literalData)[name].c_str());
} else {
if (!dataonly)
fprintf(f,"@target G%d.S%d\n",graphnum_,d->num());
d->savedata(f,correctLog());
}
}
}
/************************************************************************/
/************************************************************************/
/************************************************************************/
/************************************************************************/
/************************************************************************/
Save::Save()
: EnvironmentUser(),
nameTmpl_("output-$pz.xmgrace"),
allowPipe_(1)
{
tmpl_.gpp_= 1;
tmpl_.valid_= 0;
tmpl_.ignores_= 0;
}
int Save::loadTemplate(const char *filename, int useG0)
{
FILE *f= fopen(filename,"r");
if (!f) return 0;
Template tmpl;
tmpl.filename_= string(filename);
StringVec tmpString;
int context=0;
enum Context { GLOBAL,GRAPH,STRING };
int graph;
tmpl.gpp_= 0;
tmpl.useG0_= useG0;
std::map<int,std::map<int,std::string > > setRequests;
char line[10000], *ptr;
int maxgraph= 0;
while (!feof(f) && fgets(line,10000,f)) {
ptr= line;
if (*ptr=='#') {
tmpl.header_.append(string(line));
continue;
}
// now strip linebreak;
if (*ptr) ptr[strlen(ptr)-1]=0;
if (*ptr!='@') continue;
ptr++;
if ((*ptr=='g'|| *ptr=='G') && (isspace(ptr[2]) || isspace(ptr[3]))) {
#ifndef AVOID_XMGRACE_MAGIC_HACK
// this 'magic hack' makes unused graphs disappear but may break xmgrace
context= GRAPH;
graph= atoi(ptr+1);
if (tmpl.gpp_<graph+1) tmpl.gpp_=graph+1;
#endif
} else if (*ptr!=' ') { // endof context, if any
if (context==STRING) { // is it a param template?
string a;
if ((a= findString(&tmpString,"string def \"PARAMg"))!=string("")) {
string b(a,18);
int g,s;
sscanf(b.c_str(),"%d:%d",&g,&s);
fprintf(stderr,"found string template for graph %d, #%d!!!\n",g,s);
tmpl.params_[g][s]= tmpString;
} else {
tmpl.strings_.push_back(tmpString);
}
tmpString.clear();
}
context= GLOBAL;
}
if (!strncmp(ptr,"target ",7)) {
string name(ptr+7);
if (name.find("g",0)!=string::npos)
name.replace(name.find("g",0),1,"G");
if (name.find("s",0)!=string::npos)
name.replace(name.find("s",0),1,"S");
string data;
while(*line!='&' && !feof(f) && fgets(line,10000,f)) {
data+= string(line);
}
tmpl.data_[name]= data;
continue;
}
if (!strncmp(ptr,"type ",5)) continue;
if (!strncmp(ptr,"with ",5)) {
if (!strncmp(ptr+5,"string",6)) context=STRING;
if (ptr[5]=='g' || ptr[5]=='G') {
context= GRAPH;
graph= atoi(ptr+6);
if (graph>maxgraph) maxgraph= graph;
if (tmpl.gpp_<maxgraph+1) tmpl.gpp_=maxgraph+1;
} else {
graph=-1;
}
// fprintf(stderr,"switching to context %d, graph=%d\n",context,graph);
continue;
}
while (isspace(*ptr)) ++ptr;
// fprintf(stderr,"%d::: %s\n",context,ptr);
switch (context) {
case GLOBAL: tmpl.common_.push_back(string(ptr)); break;
case STRING: tmpString.push_back(string(ptr)); break;
case GRAPH:
if ((*ptr=='s' || *ptr=='S') && ptr[1]>='0' && ptr[1]<='9') {
// we have a set definition here:
ptr++; int set= atoi(ptr);
while (*ptr>='0'&&*ptr<='9') ++ptr; while (isspace(*ptr)) ++ptr;
string setInfo= ptr;
/* test for data requests in legend strings: */
if (setInfo.find("legend ",0)!=string::npos) {
string::size_type dPos;
string info;
if ((dPos= setInfo.find("$'",7))!=string::npos ||
(dPos= setInfo.find("$=",7))!=string::npos) {
dPos+=2;
info=setInfo.substr(dPos,setInfo.length());
// strip the request to not create great confusion
setInfo.replace(dPos,setInfo.length(),"");
if (setInfo.substr(setInfo.length()-2,2)=="$=")
setInfo.replace(setInfo.length()-2,2,"");
setInfo+= "\"";
// strip whitespace at end of info string
if (info.length()) {
int s= info.length()-1;
while (s && isspace(info[s])) s--;
info.replace(s,info.length(),"");
}
} else if (setInfo.find("$!",0)!=string::npos) {
info= "!!!literal_data!!!";
}
setRequests[graph][set]= info;
// fprintf(stderr,"info: <%s><%s>\n",setInfo.c_str(),info.c_str());
}
tmpl.sets_[graph][set].push_back(setInfo);
} else {
// settings for the graph
tmpl.graphs_[graph].push_back(ptr);
}
break;
default:
if (0) fprintf(stderr,"unknown context!!\n");
}
}
// process all set requests
tmpl.ignores_= 0;
for (int g= 0; g<=maxgraph; g++) {
StringVec v;
std::map<int,std::string>::iterator daReq;
int s;
string subtitle= findString(&(tmpl.graphs_[g]),"subtitle \"");
size_t p1=subtitle.find("\"",0)+1;
size_t p2=subtitle.rfind("\"",subtitle.length())-1;
if (subtitle[p1]==' ' && subtitle[p1+1]==' ' &&
subtitle[p2]==' ' && subtitle[p2-1]==' ' ) {
tmpl.ignore_[g]= 1;
tmpl.ignores_++;
tmpl.gpp_--;
}
if (setRequests[g].size()) {
for (s=0, daReq=setRequests[g].begin();
daReq != setRequests[g].end();
++daReq, ++s) {
if (daReq->second == "!!!literal_data!!!") {
// to be ignored..., don't add to request list
} else if (daReq->second[0] == '<') {
// ah well, we should copy data from another set!
int copyG, copyS;
char charG, charS;
if (sscanf(daReq->second.c_str(),"<%c%d.%c%d",
&charG,©G,&charS,©S)==4 &&
(charG=='g' || charG=='G') &&
(charS=='s' || charS=='S')) {
/* new copydata
fprintf(stderr,"in set G%d.S%d : <%s> -> %c %d . %c %d\n",
g,s,daReq->second.c_str(),charG,copyG,charS,copyS);
*/
tmpl.copy_[g][s].g= copyG;
tmpl.copy_[g][s].s= copyS;
} else {
fprintf(stderr,"Error in <copyset>, G%d.S%d: invalid \"%s\"!!\n",
g,s,daReq->second.c_str());
}
} else {
v.push_back(daReq->second);
// fprintf(stderr," : <%s>\n",daReq->second.c_str());
}
}
}
// only include non-ignored graphs in the request list!
if (!tmpl.ignore_[g])
tmpl.request_.push_back(v);
}
tmpl_= tmpl;
fclose(f);
return 1;
}
int Save::isCopydata(int g, int s)
{
/* new copydata
fprintf(stderr,"copy g%d.s%d from g%d.s%d ?\n",g,s,
tmpl_.copy_[g][s].g,tmpl_.copy_[g][s].s);
*/
return ((tmpl_.copy_[g][s].g>=0) && (tmpl_.copy_[g][s].s>=0));
}
void Save::regCopydata(int g, int s, Data* d)
{
Copy2Map::iterator gi;
CopyMap::iterator si;
for (gi=tmpl_.copy_.begin(); gi!=tmpl_.copy_.end(); ++gi)
for (si=gi->second.begin(); si!=gi->second.end(); ++si)
if (si->second.g==g && si->second.s==s)
si->second.src= d;
}
void Save::clearCopydata()
{
Copy2Map::iterator gi;
CopyMap::iterator si;
for (gi=tmpl_.copy_.begin(); gi!=tmpl_.copy_.end(); ++gi)
for (si=gi->second.begin(); si!=gi->second.end(); ++si)
si->second.src= 0;
}
String2Vec Save::templateDataRequestInfo()
{
return tmpl_.request_;
}
/// docs can be found in manual page "strftime(3)"
string time2string (const string &format, const struct tm *tm=0)
{
char buffer[1000];
int size;
struct timeval tv;
gettimeofday(&tv,0);
size= (tm==0) ?
strftime(buffer,999,format.c_str(),localtime((const time_t *) &tv.tv_sec)):
strftime(buffer,999,format.c_str(),tm);
buffer[999]= 0;
return string(buffer);
}
void Save::save()
{
// fprintf(stderr,"saving templated xmgr files...\n");
int graphs= graphsVec_.size();
int gps= tmpl_.gpp_;
int sheets= (graphs-1)/gps+1; // honours only application graphs
gps+= tmpl_.ignores_; // add ignored graphs to graphs per page
FILE *f= 0;
setenv("P",stringNum(sheets));
setenv("date",time2string("%d.%m.%Y"));
int isPipe = (nameTmpl_[0]=='|');
//cerr << nameTmpl_ << ": isPipe="<<isPipe<<"\n";
if (isPipe && !allowPipe_) {
isPipe= 0;
cerr <<"\n"
<<"GraceTMPL::Save::save(): Pipe output not enabled - reverting to\n"
<<" \"output-$pz.xmgrace\" for data saving\n"
<<"\n";
nameTmpl_= "output-$pz.xmgrace";
}
/*
fprintf(stderr,"now (%d graphs on %d sheets with %d per sheet)...\n",
graphs,sheets,gps);
*/
// fprintf(stderr,"inserting missing ignored graphs...\n");
int graph= 0, sheet= 0;
for (sheet= 0; sheet<sheets; sheet++) {
for (int i=0; i<gps; i++) {
Graph *g;
//new-gps fprintf(stderr,"testing graph %d on sheet %d\n",i,sheet);
if (!tmpl_.ignore_[i]) {
//new-gps fprintf(stderr,"g is graph %d/%d\n",graph,graphsVec_.size());
g= graphsVec_[graph];
} else {
g= new Graph(this);
//new-gps fprintf(stderr,"new @graph %d/%d\n",graph,graphsVec_.size());
if (graph<int(graphsVec_.size())) {
vector<Graph*>::iterator insPos(&(graphsVec_[graph]));
graphsVec_.insert(insPos,g);
} else {
graphsVec_.push_back(g);
}
}
graph++;
}
}
graphs= graphsVec_.size();
/*
fprintf(stderr,"now (%d graphs on %d sheets with %d per sheet)...\n",
graphs,sheets,gps);
*/
for (sheet= 0; sheet<sheets; sheet++) {
clearCopydata();
//env_->clear();
for (graph= 0; graph<gps && graph+sheet*gps<graphs; ++graph) {
Graph *g= graphsVec_[graph+sheet*gps];
g->setGraph(graph);
env_->add(string("g")+stringNum(graph),g->env());
if(tmpl_.useG0_)
g->saveprep(&(tmpl_.sets_[0]));
else
g->saveprep(&(tmpl_.sets_[graph]));
}
char fmt[15];
snprintf(fmt,15,"%%0%dd",int(ceil(log10(1.0*sheets))));
setenv("pz",stringNum(sheet+1,fmt));
setenv("p",stringNum(sheet+1));
string filename(expand(nameTmpl_));
setenv("filename",filename);
if (isPipe) {
filename= filename.substr(1,filename.length());
f= popen(filename.c_str(),"w");
} else {
f= fopen(filename.c_str(),"w");
}
if (!f) return;
// fprintf(stderr,"opening sheet \"%s\"\n",filename.c_str());
fprintf(f,"%s",tmpl_.header_.c_str());
StringVec::iterator daStr;
for (daStr= tmpl_.common_.begin(); daStr!= tmpl_.common_.end(); ++daStr) {
if (!strncmp(daStr->c_str(),"line on",7))
fprintf(f,"@with line\n@ %s\n",daStr->c_str());
else if (!strncmp(daStr->c_str(),"line def",8))
fprintf(f,"@%s\n",daStr->c_str());
else if (!strncmp(daStr->c_str(),"line ",5))
fprintf(f,"@ %s\n",daStr->c_str());
else
fprintf(f,"@%s\n",daStr->c_str());
}
String2Vec::iterator daString;
for (daString= tmpl_.strings_.begin(); daString!= tmpl_.strings_.end();
++daString) {
fprintf(f,"@with string\n");
StringVec::iterator daStr;
for (daStr= daString->begin(); daStr!= daString->end(); ++daStr) {
fprintf(f,"@ %s\n",expand(*daStr).c_str());
}
}
for (int graph= 0; graph<gps && graph+sheet*gps<graphs; ++graph) {
Graph *g= graphsVec_[graph+sheet*gps];
if(tmpl_.useG0_)
g->saveinfo(f,&(tmpl_.graphs_[graph]),&(tmpl_.sets_[0]),
&(tmpl_.params_[graph]));
else
g->saveinfo(f,&(tmpl_.graphs_[graph]),&(tmpl_.sets_[graph]),
&(tmpl_.params_[graph]));
}
for (int graph= 0; graph<gps && graph+sheet*gps<graphs; ++graph) {
Graph *g= graphsVec_[graph+sheet*gps];
g->savedata(f,&tmpl_.data_,!tmpl_.valid_);
}
if (isPipe) {
pclose(f);
} else {
fclose(f);
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1