view src/file-io.cc @ 2103:30e2b1c4ffd9

[project @ 1996-04-28 12:19:45 by jwe]
author jwe
date Sun, 28 Apr 1996 12:22:22 +0000
parents 36903d507b0e
children 99658f9b74c8
line wrap: on
line source

/*

Copyright (C) 1996 John W. Eaton

This file is part of Octave.

Octave 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, or (at your option) any
later version.

Octave 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 Octave; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// Originally written by John C. Campbell <jcc@bevo.che.wisc.edu>
//
// Thomas Baier <baier@ci.tuwien.ac.at> added the original versions of
// the following functions:
//
//   popen
//   pclose
//   execute       (now popen2.m)
//   sync_system   (now merged with system)
//   async_system  (now merged with system)

// Completely rewritten by John W. Eaton <jwe@bevo.che.wisc.edu>,
// April 1996.

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <climits>

#include <iostream.h>

#ifdef HAVE_UNISTD_H
#include <sys/types.h>
#include <unistd.h>
#endif

#include "defun.h"
#include "error.h"
#include "file-ops.h"
#include "help.h"
#include "oct-fstrm.h"
#include "oct-iostrm.h"
#include "oct-map.h"
#include "oct-obj.h"
#include "oct-prcstrm.h"
#include "oct-stream.h"
#include "oct-strstrm.h"
#include "pager.h"
#include "sysdep.h"
#include "syswait.h"
#include "utils.h"

void
initialize_file_io (void)
{
  octave_istream *stdin_stream = new octave_istream (&cin, "stdin");
  octave_ostream *stdout_stream = new octave_ostream (&cout, "stdout");
  octave_ostream *stderr_stream = new octave_ostream (&cerr, "stderr");

  octave_stream_list::insert (stdin_stream);
  octave_stream_list::insert (stdout_stream);
  octave_stream_list::insert (stderr_stream);
}

void
close_files (void)
{
  octave_stream_list::clear ();
}

static void
gripe_invalid_file_id (const char *fcn)
{
  ::error ("%s: invalid file id", fcn);
}

static int
fopen_mode_to_ios_mode (const string& mode)
{
  int retval = 0;

  if (! mode.empty ())
    {
      // Could probably be faster, but does it really matter?

      if (mode == "r")
	retval = ios::in;
      else if (mode == "w")
	retval = ios::out | ios::trunc;
      else if (mode == "a")
	retval = ios::out | ios::app;
      else if (mode == "r+")
	retval = ios::in | ios::out;
      else if (mode == "w+")
	retval = ios::in | ios::out | ios::trunc;
      else if (mode == "a+")
	retval = ios::in | ios::out | ios::app;
      else if (mode == "rb")
	retval = ios::in | ios::bin;
      else if (mode == "wb")
	retval = ios::out | ios::trunc | ios::bin;
      else if (mode == "ab")
	retval = ios::out | ios::app | ios::bin;
      else if (mode == "r+b")
	retval = ios::in | ios::out | ios::bin;
      else if (mode == "w+b")
	retval = ios::in | ios::out | ios::trunc | ios::bin;
      else if (mode == "a+b")
	retval = ios::in | ios::out | ios::app | ios::bin;
      else
	::error ("invalid mode specified");
    }

  return retval;
}

DEFUN (fclose, args, ,
  "fclose (FILENAME or FILENUM): close a file")
{
  double retval = -1.0;

  int nargin = args.length ();

  if (nargin == 1)
    {
      retval = (double) octave_stream_list::remove (args(0));

      if (retval < 0)
	gripe_invalid_file_id ("fclose");
    }
  else
    print_usage ("fclose");

  return retval;
}

DEFUN (fflush, args, ,
  "fflush (FILENAME or FILENUM): flush buffered data to output file")
{
  double retval = -1.0;

  int nargin = args.length ();

  if (nargin == 1)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	retval = (double) os->flush ();
      else
	gripe_invalid_file_id ("fflush");
    }
  else
    print_usage ("fflush");

  return retval;
}

DEFUN (fgetl, args, ,
  "STRING = fgetl (FILENAME or FILENUM [, LENGTH])\n\
\n\
read a string from a file")
{
  octave_value retval = -1.0;

  int nargin = args.length ();

  if (nargin == 1 || nargin == 2)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	{
	  octave_value len_arg = (nargin == 2)
	    ? args(1) : octave_value ((double) INT_MAX);

	  bool err = false;

	  string tmp = os->getl (len_arg, err);

	  if (! err)
	    retval = tmp;
	}
      else
	gripe_invalid_file_id ("fgetl");
    }
  else
    print_usage ("fgetl");

  return retval;
}

DEFUN (fgets, args, ,
  "STRING = fgets (FILENAME or FILENUM [, LENGTH])\n\
\n\
read a string from a file")
{
  octave_value retval = -1.0;

  int nargin = args.length ();

  if (nargin == 1 || nargin == 2)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	{
	  octave_value len_arg = (nargin == 2)
	    ? args(1) : octave_value ((double) INT_MAX);

	  bool err = false;

	  string tmp = os->gets (len_arg, err);

	  if (! err)
	    retval = tmp;
	}
      else
	gripe_invalid_file_id ("fgets");
    }
  else
    print_usage ("fgets");

  return retval;
}

static octave_base_stream *
do_stream_open (const string& name, const string& mode,
		const string& arch, int& fid)
{
  octave_base_stream *retval = 0;

  fid = -1;

  int md = fopen_mode_to_ios_mode (mode);

  if (! error_state)
    {
      octave_base_stream::arch_type at = octave_stream::string_to_arch (arch);

      if (! error_state)
	retval = new octave_fstream (name, md, at);
    }

  return retval;
}

static octave_base_stream *
do_stream_open (const octave_value& tc_name, const octave_value& tc_mode,
		const octave_value& tc_arch, const char *fcn, int& fid)
{
  octave_base_stream *retval = 0;

  fid = -1;

  string name = tc_name.string_value ();

  if (! error_state)
    {
      string mode = tc_mode.string_value ();

      if (! error_state)
	{
	  string arch = tc_arch.string_value ();

	  if (! error_state)
	    retval = do_stream_open (name, mode, arch, fid);
	  else
	    ::error ("%s: architecture type must be a string", fcn);
	}
      else
	::error ("%s: file mode must be a string", fcn);
    }
  else
    ::error ("%s: file name must be a string", fcn);

  return retval;
}

DEFUN (fopen, args, ,
  "FILENUM = fopen (FILENAME, MODE [, ARCH]): open a file\n\
\n\
  Valid values for mode include:\n\
\n\
    r  : open text file for reading\n\
    w  : open text file for writing; discard previous contents if any\n\
    a  : append; open or create text file for writing at end of file\n\
    r+ : open text file for update (i.e., reading and writing)\n\
    w+ : create text file for update; discard previous contents if any\n\
    a+ : append; open or create text file for update, writing at end\n\
\n\
  Update mode permits reading from and writing to the same file.")
{
  octave_value_list retval = -1.0;

  int nargin = args.length ();

  if (nargin == 1)
    {
      if (args(0).is_string () && args(0).string_value () == "all")
	{
	  return octave_stream_list::open_file_numbers ();
	}
      else
	{
	  string_vector tmp = octave_stream_list::get_info (args(0));

	  if (! error_state)
	    {
	      retval(2) = tmp(2);
	      retval(1) = tmp(1);
	      retval(0) = tmp(0);
	    }

	  return retval;
	}
    }

  if (nargin > 0 && nargin < 4)
    {
      octave_value mode = (nargin == 2 || nargin == 3)
	? args(1) : octave_value ("r");

      octave_value arch = (nargin == 3)
	? args(2) : octave_value ("native");

      int fid = -1;

      octave_base_stream *os
	= do_stream_open (args(0), mode, arch, "fopen", fid);

      if (os)
	{
	  if (os->ok () && ! error_state)
	    {
	      retval(1) = "";
	      retval(0) = (double) octave_stream_list::insert (os);
	    }
	  else
	    {
	      int errno = 0;
	      retval(1) = os->error (false, errno);
	      retval(0) = -1.0;
	    }
	}
      else
	::error ("fopen: internal error");
    }
  else
    print_usage ("fopen");

  return retval;
}

DEFUN (freport, args, ,
  "freport (): list open files and their status")
{
  octave_value_list retval;

  int nargin = args.length ();

  if (nargin > 0)
    warning ("freport: ignoring extra arguments");

  octave_stdout << octave_stream_list::list_open_files ();

  return retval;
}

DEFUN (frewind, args, ,
  "frewind (FILENAME or FILENUM): set file position at beginning of file")
{
  double retval = -1.0;

  int nargin = args.length ();

  if (nargin == 1)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	retval = (double) os->rewind ();
      else
	gripe_invalid_file_id ("frewind");
    }
  else
    print_usage ("frewind");

  return retval;
}

DEFUN (fseek, args, ,
  "fseek (FILENAME or FILENUM, OFFSET [, ORIGIN])\n\
\n\
set file position for reading or writing")
{
  double retval = -1.0;

  int nargin = args.length ();

  if (nargin == 2 || nargin == 3)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	{
	  octave_value origin_arg = (nargin == 3)
	    ? args(2) : octave_value (0.0);

	  retval = (double) os->seek (args(1), origin_arg);
	}
      else
	::error ("fseek: invalid file id");
    }
  else
    print_usage ("fseek");

  return retval;
}

DEFUN (ftell, args, ,
  "POSITION = ftell (FILENAME or FILENUM): returns the current file position")
{
  double retval = -1.0;

  int nargin = args.length ();

  if (nargin == 1)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	retval = (double) os->tell ();
      else
	gripe_invalid_file_id ("ftell");
    }
  else
    print_usage ("ftell");

  return retval;
}

DEFUN (fprintf, args, ,
  "fprintf (FILENAME or FILENUM, FORMAT, ...)")
{
  double retval = -1.0;

  int nargin = args.length ();

  if (nargin > 1)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	{
	  if (args(1).is_string ())
	    {
	      string fmt = args(1).string_value ();

	      octave_value_list tmp_args;

	      if (nargin > 2)
		{
		  tmp_args.resize (nargin-2, octave_value ());

		  for (int i = 2; i < nargin; i++)
		    tmp_args(i-2) = args(i);
		}

	      retval = os->printf (fmt, tmp_args);
	    }
	  else
	    ::error ("fprintf: format must be a string");
	}
      else
	gripe_invalid_file_id ("fprintf");
    }
  else
    print_usage ("fprintf");

  return retval;
}

DEFUN (fputs, args, ,
  "fputs (FILENAME or FILENUM, STRING)")
{
  double retval = -1.0;

  int nargin = args.length ();

  if (nargin == 2)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	retval = os->puts (args(1));
      else
	gripe_invalid_file_id ("fputs");
    }
  else
    print_usage ("fputs");

  return retval;
}

DEFUN (sprintf, args, ,
  "[s, errmsg, status] = sprintf (FORMAT, ...)")
{
  octave_value_list retval;

  retval(2) = -1.0;
  retval(1) = "unknown error";
  retval(0) = "";

  int nargin = args.length ();

  if (nargin > 0)
    {
      octave_ostrstream ostr;

      octave_stream os (&ostr);

      if (os)
	{
	  if (args(0).is_string ())
	    {
	      string fmt = args(0).string_value ();

	      octave_value_list tmp_args;

	      if (nargin > 1)
		{
		  tmp_args.resize (nargin-1, octave_value ());

		  for (int i = 1; i < nargin; i++)
		    tmp_args(i-1) = args(i);
		}

	      retval(2) = os.printf (fmt, tmp_args);
	      retval(1) = os.error ();
	      char *tmp = ostr.str ();
	      retval(0) = tmp;
	      delete [] tmp;
	    }
	  else
	    ::error ("sprintf: format must be a string");
	}
      else
	::error ("sprintf: unable to create output buffer");
    }
  else
    print_usage ("sprintf");

  return retval;
}

DEFUN (fscanf, args, ,
  "[A, B, C, ...] = fscanf (FILENAME or FILENUM, FORMAT, SIZE)")
{
  octave_value_list retval;

  int nargin = args.length ();

  if (nargin == 2 || nargin == 3)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	{
	  if (args(1).is_string ())
	    {
	      string fmt = args(1).string_value ();

	      int count = 0;

	      // XXX FIXME XXX
	      Array<int> size (2);

	      octave_value tmp = os->scanf (fmt, size, count);

	      retval(1) = (double) count;
	      retval(0) = tmp;
	    }
	  else
	    ::error ("fscanf: format must be a string");
	}
      else
	gripe_invalid_file_id ("fscanf");
    }
  else
    print_usage ("fscanf");

  return retval;
}

DEFUN (sscanf, args, ,
  "[A, COUNT, ERRMSG, INDEX] = sscanf (STRING, FORMAT, SIZE)")
{
  octave_value_list retval;

  retval(3) = -1.0;
  retval(2) = "unknown error";
  retval(1) = 0.0;
  retval(0) = Matrix ();

  int nargin = args.length ();

  if (nargin == 2 || nargin == 3)
    {
      if (args(0).is_string ())
	{
	  string data = args(0).string_value ();

	  octave_istrstream istr (data);

	  octave_stream os (&istr);

	  if (os)
	    {
	      if (args(1).is_string ())
		{
		  string fmt = args(1).string_value ();

		  int count = 0;

		  // XXX FIXME XXX
		  Array<int> size (2);

		  octave_value tmp = os.scanf (fmt, size, count);

		  retval(3) = (double) (os.tell () + 1);
		  retval(2) = os.error ();
		  retval(1) = (double) count;
		  retval(0) = tmp;
		}
	      else
		::error ("sscanf: format must be a string");
	    }
	  else
	    ::error ("sscanf: unable to create temporary input buffer");
	}
      else
	::error ("sscanf: first argument must be a string");
    }
  else
    print_usage ("sscanf");

  return retval;
}

DEFUN (fread, , ,
  "[DATA, COUNT] = fread (FILENUM, SIZE, PRECISION)\n\
\n\
 Reads data in binary form of type PRECISION from a file.\n\
\n\
 FILENUM   : file number from fopen\n\
 SIZE      : size specification for the Data matrix\n\
 PRECISION : type of data to read, valid types are\n\
\n\
             \"char\"   \"schar\" \"short\"  \"int\"  \"long\" \"float\"\n\
             \"double\" \"uchar\" \"ushort\" \"uint\" \"ulong\"\n\
\n\
 DATA      : matrix in which the data is stored\n\
 COUNT     : number of elements read")
{
  octave_value_list retval;
  // XXX IMPLEMENTME XXX
  return retval;
}

DEFUN (fwrite, , ,
  "COUNT = fwrite (FILENUM, DATA, PRECISION)\n\
\n\
 Writes data to a file in binary form of size PRECISION\n\
\n\
 FILENUM   : file number from fopen\n\
 DATA      : matrix of elements to be written\n\
 PRECISION : type of data to read, valid types are\n\
\n\
             \"char\"   \"schar\" \"short\"  \"int\"  \"long\" \"float\"\n\
             \"double\" \"uchar\" \"ushort\" \"uint\" \"ulong\"\n\
\n\
 COUNT     : number of elements written")
{
  octave_value_list retval;
  // XXX IMPLEMENTME XXX
  return retval;
}

DEFUN (feof, args, ,
  "ERROR = feof (FILENAME or FILENUM)\n\
\n\
 Returns a non zero value for an end of file condition for the\n\
 file specified by FILENAME or FILENUM from fopen")
{
  double retval = -1.0;

  int nargin = args.length ();

  if (nargin == 1)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	retval = os->eof () ? 1.0 : 0.0;
      else
	gripe_invalid_file_id ("feof");
    }
  else
    print_usage ("feof");

  return retval;
}

DEFUN (ferror, args, ,
  "ERROR = ferror (FILENAME or FILENUM, [\"clear\"])\n\
\n\
 Returns a non zero value for an error condition on the\n\
 file specified by FILENAME or FILENUM from fopen")
{
  octave_value_list retval;

  int nargin = args.length ();

  if (nargin == 1 || nargin == 2)
    {
      octave_stream *os = octave_stream_list::lookup (args(0));

      if (os)
	{
	  bool clear = false;

	  if (nargin == 2)
	    {
	      string opt = args(1).string_value ();

	      if (! error_state)
		clear = (opt == "clear");
	      else
		return retval;
	    }

	  int error_number = 0;

	  string error_message = os->error (clear, error_number);

	  retval(1) = (double) error_number;
	  retval(0) = error_message;
	}
      else
	gripe_invalid_file_id ("ferror");
    }
  else
    print_usage ("ferror");

  return retval;
}

DEFUN (popen, args, ,
  "FILENUM = popen (FILENAME, MODE)\n\
\n\
  start a process and create a pipe.  Valid values for mode are:\n\
\n\
  \"r\" : connect stdout of process to pipe\n\
  \"w\" : connect stdin of process to pipe")
{
  double retval = -1.0;

  int nargin = args.length ();

  if (nargin == 2)
    {
      string name = args(0).string_value ();

      if (! error_state)
	{
	  string mode = args(1).string_value ();

	  if (! error_state)
	    {
	      if (mode == "r")
		{
		  octave_iprocstream *ips = new octave_iprocstream (name);

		  retval = octave_stream_list::insert (ips);
		}
	      else if (mode == "w")
		{
		  octave_oprocstream *ops = new octave_oprocstream (name);

		  retval = octave_stream_list::insert (ops);
		}
	      else
		::error ("popen: invalid mode specified");
	    }
	  else
	    ::error ("popen: mode must be a string");
	}
      else
	::error ("popen: name must be a string");
    }
  else
    print_usage ("popen");

  return retval;
}

DEFUN (pclose, args, ,
  "pclose (FILENAME or FILENUM)\n\
\n\
  Close a pipe and terminate the associated process")
{
  double retval = -1.0;

  int nargin = args.length ();

  if (nargin == 1)
    {
      retval = (double) octave_stream_list::remove (args(0));

      if (retval < 0)
	gripe_invalid_file_id ("pclose");
    }
  else
    print_usage ("pclose");

  return retval;
}

DEFUN (octave_tmp_file_name, args, ,
 "octave_tmp_file_name ()")
{
  octave_value retval;

  if (args.length () == 0)
    retval = oct_tempnam ();
  else
    print_usage ("octave_tmp_file_name");

  return retval;
}

static int
convert (int x, int ibase, int obase)
{
  int retval = 0;

  int tmp = x % obase;

  if (tmp > ibase - 1)
    ::error ("umask: invalid digit");
  else
    {
      retval = tmp;
      int mult = ibase;
      while ((x = (x - tmp) / obase))
	{
	  tmp = x % obase;
	  if (tmp > ibase - 1)
	    {
	      ::error ("umask: invalid digit");
	      break;
	    }
	  retval += mult * tmp;
	  mult *= ibase;
	}
    }

  return retval;
}

DEFUN (umask, args, ,
  "umask (MASK)\n\
\n\
Change the file permission mask for file creation for the current
process.  MASK is an integer, interpreted as an octal number.  If
successful, returns the previous value of the mask (as an integer to
be interpreted as an octal number); otherwise an error message is
printed.")
{
  octave_value_list retval;

  int status = 0;

  if (args.length () == 1)
    {
      double dmask = args(0).double_value ();

      if (error_state)
	{
	  status = -1;
	  ::error ("umask: expecting integer argument");
	}
      else
	{
	  int mask = NINT (dmask);

	  if ((double) mask != dmask || mask < 0)
	    {
	      status = -1;
	      ::error ("umask: MASK must be a positive integer value");
	    }
	  else
	    {
	      int oct_mask = convert (mask, 8, 10);

	      if (! error_state)
		status = convert (oct_umask (oct_mask), 10, 8);
	    }
	}
    }
  else
    print_usage ("umask");

  if (status >= 0)
    retval(0) = (double) status;

  return retval;
}

/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; End: ***
*/