/*****
* runfile.in
*
* Runtime functions for file operations.
*
*****/

file*    => primFile()

#include "fileio.h"
#include "callable.h"
#include "triple.h"
#include "array.h"
#include "seconds.h"

#if defined(_WIN32)
#include <io.h>
#else
#include <unistd.h>
#endif

using namespace camp;
using namespace settings;
using namespace vm;

string commentchar="#";

// Autogenerated routines:


bool ==(file *a, file *b)
{
 return a == b;
}

bool !=(file *a, file *b)
{
 return a != b;
}

file* :nullFile()
{
 return &camp::nullfile;
}

file* input(string name=emptystring, bool check=true,
           string comment=commentchar, string mode=emptystring)
{
 file *f=NULL;
 if(mode == "binary")
   f=new ibfile(name,check);
 else if(mode == "xdr" || mode == "xdrgz") {
#ifdef HAVE_LIBTIRPC
   if(mode == "xdr")
     f=new ixfile(name,check);
   else if(mode == "xdrgz")
     f=new igzxfile(name,check);
#else
   ostringstream buf;
   buf << name << ": XDR read support not enabled";
   error(buf);
#endif
 } else if(mode == "") {
   char c=comment.empty() ? (char) 0 : comment[0];
   f=new camp::ifile(name,c,check);
 } else {
   f=NULL;
   ostringstream buf;
   buf << name << ": invalid file mode '" << mode << "'";
   error(buf);
 }

 f->open();
 return f;
}

file* output(string name=emptystring, bool update=false,
            string comment=commentchar, string mode=emptystring)
{
 file *f=NULL;
 if(mode == "pipe") {
   f=new opipe(name);
 } else if(mode == "binary") {
   if(update) f=new iobfile(name);
   else f=new obfile(name);
 } else if(mode == "xdr") {
#ifdef HAVE_LIBTIRPC
   if(update)
     f=new ioxfile(name);
   else f=new oxfile(name);
#else
   ostringstream buf;
   buf << name << ": XDR write support not enabled";
   error(buf);
#endif
 } else if(mode == "") {
   if(update) {
     char c=comment.empty() ? (char) 0 : comment[0];
     f=new iofile(name,c);
   } else f=new ofile(name);
 } else {
   f=NULL;
   ostringstream buf;
   buf << name << ": invalid file mode '" << mode << "'";
   error(buf);
 }

 f->open();
 if(update) f->seek(0,false);

 return f;
}

bool eof(file *f)
{
 return f->eof();
}

bool eol(file *f)
{
 return f->eol();
}

bool error(file *f)
{
 return f->error();
}

void clear(file *f)
{
 f->clear();
}

void close(file *f)
{
 f->close();
}

Int precision(file *f=NULL, Int digits=0)
{
 if(f == 0) f=&camp::Stdout;
 return f->precision(digits);
}

void flush(file *f)
{
 f->flush();
}

string getc(file *f)
{
 char c=0;
 if(f->isOpen()) f->read(c);
 return string(1,c);
}

Int tell(file *f)
{
 return f->tell();
}

void seek(file *f, Int pos)
{
 f->seek(pos,pos >= 0);
}

void seekeof(file *f)
{
 f->seek(0,false);
}

string :namePart(file f)
{
 return f.filename();
}

string :modePart(file f)
{
 return f.FileMode();
}

// Set file dimensions
file* :dimensionSetHelper(Int nx=-1, Int ny=-1, Int nz=-1, file *f)
{
 f->dimension(nx,ny,nz);
 return f;
}

callable* :dimensionSet(file *f)
{
 return new thunk(new bfunc(dimensionSetHelper),f);
}

array * :dimensionPart(file f)
{
 array *a=new array(3);
 (*a)[0]=f.Nx();
 (*a)[1]=f.Ny();
 (*a)[2]=f.Nz();
 return a;
}

// Set file f to read arrays in line-at-a-time mode
file* :lineSetHelper(bool b=true, file *f)
{
 f->LineMode(b);
 return f;
}

callable* :lineSet(file *f)
{
 return new thunk(new bfunc(lineSetHelper),f);
}

bool :linePart(file f)
{
 return f.LineMode();
}

// Set file to read comma-separated values
file* :csvSetHelper(bool b=true, file *f)
{
 f->CSVMode(b);
 return f;
}

callable* :csvSet(file *f)
{
 return new thunk(new bfunc(csvSetHelper),f);
}

bool :csvPart(file f)
{
 return f.CSVMode();
}

// Set file to read whitespace-separated values
file* :wordSetHelper(bool b=true, file *f)
{
 f->WordMode(b);
 return f;
}

callable* :wordSet(file *f)
{
 return new thunk(new bfunc(wordSetHelper),f);
}

bool :wordPart(file f)
{
 return f.WordMode();
}

// Set file to read/write single precision real XDR values.
file* :singlerealSetHelper(bool b=true, file *f)
{
 f->SingleReal(b);
 return f;
}

callable* :singlerealSet(file *f)
{
 return new thunk(new bfunc(singlerealSetHelper),f);
}

bool :singlerealPart(file f)
{
 return f.SingleReal();
}

// Set file to read/write single precision int XDR values.
file* :singleintSetHelper(bool b=true, file *f)
{
 f->SingleInt(b);
 return f;
}

callable* :singleintSet(file *f)
{
 return new thunk(new bfunc(singleintSetHelper),f);
}

bool :singleintPart(file f)
{
 return f.SingleInt();
}

// Set file to read/write signed int XDR values.
file* :signedintSetHelper(bool b=true, file *f)
{
 f->SignedInt(b);
 return f;
}

callable* :signedintSet(file *f)
{
 return new thunk(new bfunc(signedintSetHelper),f);
}

bool :signedintPart(file f)
{
 return f.SignedInt();
}

// Set file to read an arrayi (i int sizes followed by an i-dimensional array)
file* :readSetHelper(Int i, file *f)
{
 switch(i) {
   case 1:
   f->dimension(-2);
   break;

   case 2:
   f->dimension(-2,-2);
   break;

   case 3:
   f->dimension(-2,-2,-2);
   break;

   default:
   f->dimension();
 }

 return f;
}

callable* :readSet(file *f)
{
 return new thunk(new bfunc(readSetHelper),f);
}

// Delete file named s.
Int delete(string s)
{
 s=outpath(s);
 Int rc=unlink(s.c_str());
 if(rc == 0 && verbose > 0)
   cout << "Deleted " << s << endl;
 return rc;
}

// Rename file "from" to file "to".
Int rename(string from, string to)
{
 from=outpath(from);
 to=outpath(to);
 Int rc=renameOverwrite(from.c_str(),to.c_str());
 if(rc == 0 && verbose > 0)
   cout << "Renamed " << from << " to " << to << endl;
 return rc;
}

// Create a uniquely named temporary file.
string mktemp(string s)
{
 string baseTemplate=s+"XXXXXX";
 char *S=StrdupMalloc(baseTemplate);
 bool success=true;
#if defined(_WIN32)
 if (_mktemp_s(S,baseTemplate.length()+1) != 0)
 {
   success = false;
 }
 FILE* fp;
 if (success && (fopen_s(&fp,S,"w") != 0))
 {
   success = false;
 }
#else
 int fd=mkstemp(S);
 if (fd < 0)
 {
   success = false;
 }
#endif
 if(!success)
 {
   ostringstream buf;
   buf << "Could not create unique temporary filename based on " << s;
   error(buf);
 }
 string T(S);
 free(S);

#if defined(_WIN32)
 bool closeSuccess = fclose(fp) == 0;
#else
 bool closeSuccess = close(fd) == 0;
#endif

 if (!closeSuccess)
 {
   ostringstream buf;
   buf << "Could not finalize temporary file based on " << s;
   error(buf);
 }
 return T;
}