aboutsummaryrefslogtreecommitdiffstats
path: root/file.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'file.cpp')
-rw-r--r--file.cpp903
1 files changed, 903 insertions, 0 deletions
diff --git a/file.cpp b/file.cpp
new file mode 100644
index 0000000..bf68bd6
--- /dev/null
+++ b/file.cpp
@@ -0,0 +1,903 @@
+// file.cpp
+// Revision 9-jan-2005
+
+#include "blassic.h"
+#include "file.h"
+#include "trace.h"
+#include "error.h"
+#include "var.h"
+
+//#include "cursor.h"
+//#include "edit.h"
+//#include "graphics.h"
+
+#include "sysvar.h"
+//#include "socket.h"
+#include "util.h"
+using util::to_string;
+
+#include <fstream>
+#include <iomanip>
+#include <sstream>
+#include <stdexcept>
+#include <algorithm>
+
+// para strerror (errno)
+#include <string.h>
+#include <errno.h>
+
+#include <iostream>
+using std::cerr;
+using std::endl;
+
+#include <cassert>
+#define ASSERT assert
+
+//***********************************************
+// Auxiliary functions
+//***********************************************
+
+namespace {
+
+BlInteger lengthoffileread (std::fstream & fs)
+{
+ // WARNING: The C++ standard does not guarantee that
+ // this method obtains the length of the file.
+ std::streamsize old= fs.tellg ();
+ fs.seekg (0, std::ios::end);
+ std::streamsize l= fs.tellg ();
+ fs.seekg (old, std::ios::beg);
+ // Not leave the file unusable in case something were wrong.
+ fs.clear ();
+ return static_cast <BlInteger> (l);
+}
+
+BlInteger lengthoffilewrite (std::fstream & fs)
+{
+ // WARNING: The C++ standard does not guarantee that
+ // this method obtains the length of the file.
+ std::streamsize old= fs.tellp ();
+ fs.seekp (0, std::ios::end);
+ std::streamsize l= fs.tellp ();
+ fs.seekp (old, std::ios::beg);
+ // Not leave the file unusable in case something were wrong.
+ fs.clear ();
+ return static_cast <BlInteger> (l);
+}
+
+} // namespace
+
+namespace blassic {
+
+namespace file {
+
+//***********************************************
+// BlFile
+//***********************************************
+
+BlFile::BlFile (OpenMode nmode) :
+ mode (nmode),
+ cDelimiter (','),
+ cQuote ('"'),
+ cEscape ('\0')
+{
+}
+
+BlFile::~BlFile ()
+{
+}
+
+void BlFile::closein ()
+ { throw ErrFileMode; }
+void BlFile::closeout ()
+ { throw ErrFileMode; }
+
+void BlFile::reset (int, int, int, int)
+ { throw ErrFileMode; }
+
+bool BlFile::istextwindow () const
+{
+ return false;
+}
+
+bool BlFile::eof ()
+{
+ if (showdebuginfo () )
+ cerr << "This file type does not implement EOF" << endl;
+ throw ErrFileMode;
+}
+
+size_t BlFile::loc ()
+ { throw ErrFileMode; }
+void BlFile::flush ()
+ { throw ErrFileMode; }
+void BlFile::getline (std::string &, bool)
+ { throw ErrFileMode; }
+void BlFile::outstring (const std::string &)
+ { throw ErrFileMode; }
+void BlFile::outchar (char)
+ { throw ErrFileMode; }
+
+#if 0
+
+void BlFile::outnumber (BlNumber n)
+{
+ //outstring (to_string (n) );
+ std::ostringstream oss;
+ oss << std::setprecision (16) << n;
+ outstring (oss.str () );
+}
+
+void BlFile::outinteger (BlInteger n)
+{
+ outstring (to_string (n) );
+}
+
+void BlFile::outlinenumber (BlLineNumber l)
+{
+ std::ostringstream oss;
+ oss << std::setw (7) << l;
+ outstring (oss.str () );
+}
+
+#endif
+
+BlFile & operator << (BlFile & bf, const std::string & str)
+{
+ TRACEFUNC (tr, "operator << (BlFile &, const string &)");
+
+ bf.outstring (str);
+ return bf;
+}
+
+BlFile & operator << (BlFile & bf, char c)
+{
+ bf.outchar (c);
+ return bf;
+}
+
+BlFile & operator << (BlFile & bf, BlNumber n)
+{
+ //bf.outnumber (n);
+ std::ostringstream oss;
+ oss << std::setprecision (16) << n;
+ bf.outstring (oss.str () );
+ return bf;
+}
+
+BlFile & operator << (BlFile & bf, BlInteger n)
+{
+ //bf.outinteger (n);
+ bf.outstring (to_string (n) );
+ return bf;
+}
+
+BlFile & operator << (BlFile & bf, BlLineNumber l)
+{
+ //bf.outlinenumber (l);
+ std::ostringstream oss;
+ oss << std::setw (7) << l;
+ bf.outstring (oss.str () );
+ return bf;
+}
+
+BlFile & operator << (BlFile & bf, unsigned short n)
+{
+ bf.outstring (to_string (n) );
+ return bf;
+}
+
+void BlFile::putspaces (size_t n)
+{
+ outstring (std::string (n, ' ') );
+}
+
+void BlFile::tab ()
+{
+ outchar ('\t');
+}
+
+void BlFile::tab (size_t n)
+{
+ // Provisional
+ outstring (std::string (n, ' ') );
+}
+
+void BlFile::endline ()
+{
+ outchar ('\n');
+}
+
+void BlFile::put (size_t)
+{ throw ErrFileMode; }
+
+void BlFile::get (size_t)
+{ throw ErrFileMode; }
+
+void BlFile::field_clear ()
+{ throw ErrFileMode; }
+
+void BlFile::field (const std::vector <field_element> &)
+{ throw ErrFileMode; }
+
+void BlFile::field_append (const std::vector <field_element> &)
+{ throw ErrFileMode; }
+
+// assign doesn't throw because we call it for all open files,
+// those that are no random files or does nor have the var in
+// their fields just ignore it.
+bool BlFile::assign (const std::string &, const Dimension &,
+ const std::string &, Align)
+{ return false; }
+bool BlFile::assign_mid (const std::string &, const Dimension &,
+ const std::string &, size_t, std::string::size_type)
+{ return false; }
+
+size_t BlFile::getwidth () const
+{ throw ErrFileMode; }
+
+void BlFile::movecharforward ()
+{ throw ErrFileMode; }
+
+void BlFile::movecharforward (size_t)
+{ throw ErrFileMode; }
+
+void BlFile::movecharback ()
+{ throw ErrFileMode; }
+
+void BlFile::movecharback (size_t)
+{ throw ErrFileMode; }
+
+void BlFile::movecharup ()
+{ throw ErrFileMode; }
+
+void BlFile::movecharup (size_t)
+{ throw ErrFileMode; }
+
+void BlFile::movechardown ()
+{ throw ErrFileMode; }
+
+void BlFile::movechardown (size_t)
+{ throw ErrFileMode; }
+
+void BlFile::showcursor ()
+{ throw ErrFileMode; }
+
+void BlFile::hidecursor ()
+{ throw ErrFileMode; }
+
+std::string BlFile::getkey ()
+{ throw ErrFileMode; }
+
+std::string BlFile::inkey ()
+{ throw ErrFileMode; }
+
+std::string BlFile::read (size_t)
+{ throw ErrFileMode; }
+
+void BlFile::gotoxy (int, int)
+{ throw ErrFileMode; }
+
+void BlFile::setcolor (int)
+{ throw ErrFileMode; }
+
+int BlFile::getcolor ()
+{ throw ErrFileMode; }
+
+void BlFile::setbackground (int)
+{ throw ErrFileMode; }
+
+int BlFile::getbackground ()
+{ throw ErrFileMode; }
+
+void BlFile::cls ()
+{ throw ErrFileMode; }
+
+std::string BlFile::copychr (BlChar, BlChar)
+{ throw ErrFileMode; }
+
+int BlFile::pos ()
+{ throw ErrFileMode; }
+
+int BlFile::vpos ()
+{ throw ErrFileMode; }
+
+void BlFile::tag ()
+{ throw ErrFileMode; }
+
+void BlFile::tagoff ()
+{ /* Ignored */ }
+
+bool BlFile::istagactive ()
+{ return false; }
+
+void BlFile::inverse (bool)
+{ throw ErrFileMode; }
+
+bool BlFile::getinverse ()
+{ throw ErrFileMode; }
+
+void BlFile::bright (bool)
+{ throw ErrFileMode; }
+
+bool BlFile::getbright ()
+{ throw ErrFileMode; }
+
+void BlFile::setwidth (size_t )
+{ }
+
+void BlFile::setmargin (size_t )
+{ }
+
+BlInteger BlFile::lof ()
+{ throw ErrFileMode; }
+
+bool BlFile::poll ()
+{
+ return false;
+}
+
+void BlFile::scroll (int)
+{ throw ErrFileMode; }
+
+
+//***********************************************
+// BlFileOut
+//***********************************************
+
+BlFileOut::BlFileOut () : BlFile (Output)
+{ }
+
+BlFileOut::BlFileOut (OpenMode mode) : BlFile (mode)
+{ }
+
+void BlFileOut::flush ()
+{
+ ofs () << std::flush;
+}
+
+void BlFileOut::outstring (const std::string & str)
+{
+ ofs () << str;
+}
+
+void BlFileOut::outchar (char c)
+{
+ ofs () << c;
+}
+
+#if 0
+
+void BlFileOut::outnumber (BlNumber n)
+{
+ ofs () << n;
+}
+
+void BlFileOut::outinteger (BlInteger n)
+{
+ ofs () << n;
+}
+
+void BlFileOut::outlinenumber (BlLineNumber l)
+{
+ ofs () << std::setw (7) << l;
+}
+
+#endif
+
+//***********************************************
+// BlFileOutString
+//***********************************************
+
+BlFile * newBlFileOutString ()
+{
+ return new BlFileOutString;
+}
+
+BlFileOutString::BlFileOutString ()
+{ }
+
+std::string BlFileOutString::str ()
+{
+ return oss.str ();
+}
+
+std::ostream & BlFileOutString::ofs ()
+{
+ return oss;
+}
+
+//***********************************************
+// BlFileOutput
+//***********************************************
+
+class BlFileOutput : public BlFileOut {
+public:
+ BlFileOutput (std::ostream & os);
+ bool isfile () const { return true; }
+private:
+ std::ostream & ofs ();
+ std::ostream & os;
+};
+
+class BlFileRegular : public BlFileOut {
+public:
+ BlFileRegular (const std::string & name, OpenMode mode);
+ bool isfile () const { return true; }
+ void getline (std::string & str, bool endline= true);
+ bool eof ();
+ void flush ();
+ virtual std::string getkey ();
+ virtual std::string inkey ();
+ std::string read (size_t n);
+ BlInteger lof ();
+private:
+ std::ostream & ofs ();
+ std::fstream fs;
+};
+
+BlFile * newBlFileOutput (std::ostream & os)
+{
+ return new BlFileOutput (os);
+}
+
+BlFileOutput::BlFileOutput (std::ostream & os) :
+ os (os)
+{
+}
+
+std::ostream & BlFileOutput::ofs ()
+{
+ return os;
+}
+
+//***********************************************
+// BlFileRegular
+//***********************************************
+
+BlFile * newBlFileRegular (const std::string & name, OpenMode mode)
+{
+ return new BlFileRegular (name, mode);
+}
+
+BlFileRegular::BlFileRegular (const std::string & name, OpenMode nmode) :
+ BlFileOut (nmode)
+{
+ TRACEFUNC (tr, "BlFileRegular::BlFileRegular");
+
+ using std::ios;
+
+ ios::openmode mode= ios::in;
+ switch (nmode & ~ Binary)
+ {
+ case Input:
+ mode= ios::in; break;
+ case Output:
+ mode= ios::out; break;
+ case Append:
+ //mode= ios::out | ios::ate; break;
+ mode= ios::out | ios::app; break;
+ default:
+ if (showdebuginfo () )
+ cerr << "Unexpected mode value" << endl;
+ TRMESSAGE (tr, std::string ("Invalid mode ") +
+ util::to_string (nmode) );
+ throw ErrBlassicInternal;
+ }
+ if (nmode & Binary)
+ mode|= ios::binary;
+ fs.open (name.c_str (), mode);
+ if (! fs.is_open () )
+ {
+ if (showdebuginfo () )
+ cerr << "open (" << name << ", " << mode <<
+ ") failed: " << strerror (errno) <<
+ endl;
+ throw ErrFileNotFound;
+ }
+}
+
+bool BlFileRegular::eof ()
+{
+ int c= fs.get ();
+ if (! fs || c == EOF)
+ return true;
+ fs.unget ();
+ return false;
+}
+
+void BlFileRegular::flush ()
+{
+ fs << std::flush;
+}
+
+void BlFileRegular::getline (std::string & str, bool)
+{
+ std::getline (fs, str);
+ if (! fs)
+ {
+ if (fs.eof () )
+ throw ErrPastEof;
+ else
+ throw ErrFileRead;
+ }
+}
+
+std::string BlFileRegular::getkey ()
+{
+ return read (1);
+}
+
+std::string BlFileRegular::inkey ()
+{
+ int c= fs.get ();
+ if (! fs || c == EOF)
+ {
+ fs.clear (); // Allow further reads.
+ return std::string ();
+ }
+ return std::string (1, static_cast <char> (c) );
+}
+
+std::string BlFileRegular::read (size_t n)
+{
+ util::auto_buffer <char> buf (n);
+ fs.read (buf, n);
+ if (! fs)
+ {
+ if (fs.eof () )
+ throw ErrPastEof;
+ else
+ throw ErrFileRead;
+ }
+ return std::string (buf, n);
+}
+
+std::ostream & BlFileRegular::ofs ()
+{
+ return fs;
+}
+
+BlInteger BlFileRegular::lof ()
+{
+ if (getmode () & Input)
+ return lengthoffileread (fs);
+ else
+ return lengthoffilewrite (fs);
+}
+
+//***********************************************
+// BlFileRandom
+//***********************************************
+
+class BlFileRandom : public BlFile {
+public:
+ BlFileRandom (const std::string & name, size_t record_len);
+ bool isfile () const { return true; }
+ bool eof ();
+ virtual size_t loc ();
+ void put (size_t pos);
+ void get (size_t pos);
+ void field_clear ();
+ void field (const std::vector <field_element> & elem);
+ void field_append (const std::vector <field_element> & elem);
+ virtual bool assign (const std::string & name, const Dimension & dim,
+ const std::string & value, Align align);
+ virtual bool assign_mid (const std::string & name,
+ const Dimension & dim,
+ const std::string & value,
+ size_t inipos, std::string::size_type len);
+ struct field_chunk {
+ std::string name;
+ Dimension dim;
+ size_t pos;
+ size_t size;
+ inline void getvar (char * buf) const;
+ };
+ typedef std::vector <field_chunk> vchunk;
+ BlInteger lof ();
+private:
+ std::fstream fs;
+ size_t len;
+ size_t len_assigned;
+ bool used;
+ size_t actual;
+ //auto_array buf;
+ util::auto_buffer <char> buf;
+ vchunk chunk;
+};
+
+BlFile * newBlFileRandom (const std::string & name, size_t record_len)
+{
+ return new BlFileRandom (name, record_len);
+}
+
+inline void BlFileRandom::field_chunk::getvar (char * buf) const
+{
+ if (dim.empty () )
+ assignvarstring (name, std::string (buf + pos, size) );
+ else
+ assigndimstring (name, dim, std::string (buf + pos, size) );
+}
+
+BlFileRandom::BlFileRandom (const std::string & name, size_t record_len) :
+ BlFile (Random),
+ len (record_len),
+ len_assigned (0),
+ used (false),
+ actual (0),
+ buf (record_len)
+{
+ using std::ios;
+
+ const ios::openmode iobin (ios::in | ios::out | ios::binary);
+ fs.open (name.c_str (), iobin);
+ if (! fs.is_open () )
+ {
+ // This can be necessary or not depending of the compiler
+ // to create the file if not exist. We test it, anyway.
+ if (errno == ENOENT)
+ {
+ fs.clear ();
+ fs.open (name.c_str (), ios::out | ios::binary);
+ if (fs.is_open () )
+ {
+ fs.close ();
+ fs.open (name.c_str (), iobin);
+ }
+
+ }
+ if (! fs.is_open () )
+ {
+ if (showdebuginfo () )
+ cerr << "open " << name << " failed: " <<
+ strerror (errno) << endl;
+ throw ErrFileNotFound;
+ }
+ }
+ std::fill_n (buf.begin (), len, '\0');
+}
+
+bool BlFileRandom::eof ()
+{
+ fs.clear ();
+ fs.seekg (actual * len);
+ if (! fs)
+ {
+ if (fs.eof () )
+ return true;
+ throw ErrFileRead;
+ }
+ char testchar;
+ fs.read (& testchar, 1);
+ if (! fs)
+ {
+ if (fs.eof () )
+ return true;
+ throw ErrFileRead;
+ }
+ return fs.gcount () != 1;
+}
+
+size_t BlFileRandom::loc ()
+{
+ return used ? actual + 1 : size_t (0);
+}
+
+
+void BlFileRandom::put (size_t pos)
+{
+ used= true;
+ if (pos != 0)
+ {
+ actual= pos - 1;
+ fs.seekp (actual * len);
+ }
+ fs.write (buf, len);
+ ++actual;
+}
+
+namespace {
+
+class GetVar {
+public:
+ GetVar (char * buf) : buf (buf) { }
+ void operator () (const BlFileRandom::field_chunk & chunk)
+ {
+ chunk.getvar (buf);
+ }
+private:
+ char * buf;
+};
+
+} // namespace
+
+void BlFileRandom::get (size_t pos)
+{
+ using std::string;
+
+ used= true;
+ if (pos != 0)
+ actual= pos - 1;
+ fs.clear ();
+ fs.seekg (actual * len);
+ fs.read (buf, len);
+ std::streamsize r= fs.gcount ();
+ if (r < std::streamsize (len) )
+ std::fill_n (buf.begin () + r, len - r, ' ');
+ std::for_each (chunk.begin (), chunk.end (), GetVar (buf) );
+ ++actual;
+}
+
+namespace {
+
+class MakeChunk {
+public:
+ MakeChunk (size_t len, size_t & pos) :
+ len (len),
+ pos (pos)
+ { }
+ BlFileRandom::field_chunk operator ()
+ (const BlFile::field_element & elem)
+ {
+ size_t size= elem.size;
+ BlFileRandom::field_chunk fc;
+ fc.name= elem.name;
+ fc.dim= elem.dim;
+ fc.pos= pos;
+ fc.size= size;
+ pos+= size;
+ if (pos > len)
+ throw ErrFieldOverflow;
+ return fc;
+ }
+ size_t getpos () const { return pos; }
+private:
+ const size_t len;
+ size_t & pos;
+};
+
+} // namespace
+
+void BlFileRandom::field_clear ()
+{
+ chunk.clear ();
+ len_assigned= 0;
+}
+
+void BlFileRandom::field_append (const std::vector <field_element> & elem)
+{
+ TRACEFUNC (tr, "BlFileRandom::field_append");
+
+ // Create the field chunks.
+ //MakeChunk maker (len, len_assigned);
+ std::transform (elem.begin (), elem.end (),
+ std::back_inserter (chunk),
+ MakeChunk (len, len_assigned) );
+ // Check the assigned mark.
+ TRMESSAGE (tr, std::string ("len_assigned= ") +
+ util::to_string (len_assigned) );
+
+ // Assign initial values to the field variables (filled with
+ // zeroes if the file is opened and not touched).
+ // Do it for all fields, not only the added now.
+ std::for_each (chunk.begin (), chunk.end (), GetVar (buf) );
+}
+
+void BlFileRandom::field (const std::vector <field_element> & elem)
+{
+ field_clear ();
+ field_append (elem);
+}
+
+namespace {
+
+class field_var_is {
+public:
+ field_var_is (const std::string & name, const Dimension & dim) :
+ name (name), dim (dim)
+ { }
+ bool operator () (const BlFileRandom::field_chunk & chunk)
+ {
+ return chunk.name == name && chunk.dim == dim;
+ }
+private:
+ const std::string & name;
+ const Dimension & dim;
+};
+
+} // namespace
+
+bool BlFileRandom::assign (const std::string & name, const Dimension & dim,
+ const std::string & value, Align align)
+{
+ vchunk::iterator pe= std::find_if (chunk.begin (), chunk.end (),
+ field_var_is (name, dim) );
+ if (pe == chunk.end () )
+ return false;
+ if (pe->size == 0)
+ return true;
+ char * init= buf + pe->pos;
+ std::string str;
+
+ // Changed the behaviour: now does the same as gwbasic.
+
+ #if 0
+ std::string::size_type l= value.size ();
+ if (align == AlignLeft)
+ {
+ if (l < pe->size)
+ str= value + std::string (pe->size - l, ' ');
+ else
+ str= value.substr (0, pe->size);
+ }
+ else
+ {
+ if (l < pe->size)
+ str= std::string (pe->size - l, ' ') + value;
+ else
+ str= value.substr (l - pe->size);
+ }
+ #else
+ if (align == AlignLeft)
+ str= util::stringlset (value, pe->size);
+ else
+ str= util::stringrset (value, pe->size);
+ #endif
+ ASSERT (str.size () == pe->size);
+ #if 0
+ std::copy (str.begin (), str.end (), init);
+ #else
+ str.copy (init, pe->size);
+ #endif
+ // Now also assign to the standalone var, so that the
+ // value assiged can be checked.
+ if (dim.empty () )
+ assignvarstring (name, str);
+ else
+ assigndimstring (name, dim, str);
+ return true;
+}
+
+bool BlFileRandom::assign_mid (const std::string & name,
+ const Dimension & dim,
+ const std::string & value,
+ size_t inipos, std::string::size_type len)
+{
+ vchunk::iterator pe= std::find_if (chunk.begin (), chunk.end (),
+ field_var_is (name, dim) );
+ if (pe == chunk.end () )
+ return false;
+ const size_t size= pe->size;
+ if (size == 0)
+ return true;
+ if (inipos >= size)
+ return true;
+ char * const init= buf + pe->pos;
+ char * const initmid= init + inipos;
+ size_t l= size - inipos;
+ if (len > l)
+ len= l;
+ std::string str= value.substr (0, len);
+ if (str.size () < len)
+ str+= std::string (len - str.size (), ' ');
+ str.copy (initmid, str.size () );
+ // Now also assign to the standalone var, so that the
+ // value assiged can be checked.
+ if (dim.empty () )
+ assignvarstring (name, std::string (init, size) );
+ else
+ assigndimstring (name, dim, std::string (init, size) );
+ return true;
+}
+
+BlInteger BlFileRandom::lof ()
+{
+ return lengthoffileread (fs);
+}
+
+} // namespace file
+
+} // namespace blassic
+
+// Fin de file.cpp
Un proyecto texto-plano.xyz