/*
FILE:     FileIo.C
*/
/*
VERSION:  4
*/
/*
DATE:     6/17/2002
*/
/*
  Comments and Questions to: sw-help@rcsb.rutgers.edu
*/
/*
COPYRIGHT 1999-2002 Rutgers - The State University of New Jersey

This software is provided WITHOUT WARRANTY OF MERCHANTABILITY OR
FITNESS FOR A PARTICULAR PURPOSE OR ANY OTHER WARRANTY, EXPRESS OR
IMPLIED.  RUTGERS MAKE NO REPRESENTATION OR WARRANTY THAT THE
SOFTWARE WILL NOT INFRINGE ANY PATENT, COPYRIGHT OR OTHER
PROPRIETARY RIGHT.

The user of this software shall indemnify, hold harmless and defend
Rutgers, its governors, trustees, officers, employees, students,
agents and the authors against any and all claims, suits,
losses, liabilities, damages, costs, fees, and expenses including
reasonable attorneys' fees resulting from or arising out of the
use of this software.  This indemnification shall include, but is
not limited to, any and all claims alleging products liability.
*/
/*
               PDB SOFTWARE LICENSE AGREEMENT

BY CLICKING THE ACCEPTANCE BUTTON OR INSTALLING OR USING 
THIS "SOFTWARE, THE INDIVIDUAL OR ENTITY LICENSING THE  
SOFTWARE ("LICENSEE") IS CONSENTING TO BE BOUND BY AND IS 
BECOMING A PARTY TO THIS AGREEMENT.  IF LICENSEE DOES NOT 
AGREE TO ALL OF THE TERMS OF THIS AGREEMENT
THE LICENSEE MUST NOT INSTALL OR USE THE SOFTWARE.

1. LICENSE AGREEMENT

This is a license between you ("Licensee") and the Protein Data Bank (PDB) 
at Rutgers, The State University of New Jersey (hereafter referred to 
as "RUTGERS").   The software is owned by RUTGERS and protected by 
copyright laws, and some elements are protected by laws governing 
trademarks, trade dress and trade secrets, and may be protected by 
patent laws. 

2. LICENSE GRANT

RUTGERS grants you, and you hereby accept, non-exclusive, royalty-free 
perpetual license to install, use, modify, prepare derivative works, 
incorporate into other computer software, and distribute in binary 
and source code format, or any derivative work thereof, together with 
any associated media, printed materials, and on-line or electronic 
documentation (if any) provided by RUTGERS (collectively, the "SOFTWARE"), 
subject to the following terms and conditions: (i) any distribution 
of the SOFTWARE shall bind the receiver to the terms and conditions 
of this Agreement; (ii) any distribution of the SOFTWARE in modified 
form shall clearly state that the SOFTWARE has been modified from 
the version originally obtained from RUTGERS.  

2. COPYRIGHT; RETENTION OF RIGHTS.  

The above license grant is conditioned on the following: (i) you must 
reproduce all copyright notices and other proprietary notices on any 
copies of the SOFTWARE and you must not remove such notices; (ii) in 
the event you compile the SOFTWARE, you will include the copyright 
notice with the binary in such a manner as to allow it to be easily 
viewable; (iii) if you incorporate the SOFTWARE into other code, you 
must provide notice that the code contains the SOFTWARE and include 
a copy of the copyright notices and other proprietary notices.  All 
copies of the SOFTWARE shall be subject to the terms of this Agreement.  

3. NO MAINTENANCE OR SUPPORT; TREATMENT OF ENHANCEMENTS 

RUTGERS is under no obligation whatsoever to: (i) provide maintenance 
or support for the SOFTWARE; or (ii) to notify you of bug fixes, patches, 
or upgrades to the features, functionality or performance of the 
SOFTWARE ("Enhancements") (if any), whether developed by RUTGERS 
or third parties.  If, in its sole discretion, RUTGERS makes an 
Enhancement available to you and RUTGERS does not separately enter 
into a written license agreement with you relating to such bug fix, 
patch or upgrade, then it shall be deemed incorporated into the SOFTWARE 
and subject to this Agreement. You are under no obligation whatsoever 
to provide any Enhancements to RUTGERS or the public that you may 
develop over time; however, if you choose to provide your Enhancements 
to RUTGERS, or if you choose to otherwise publish or distribute your 
Enhancements, in source code form without contemporaneously requiring 
end users or RUTGERS to enter into a separate written license agreement 
for such Enhancements, then you hereby grant RUTGERS a non-exclusive,
royalty-free perpetual license to install, use, modify, prepare
derivative works, incorporate into the SOFTWARE or other computer
software, distribute, and sublicense your Enhancements or derivative
works thereof, in binary and source code form.

4. FEES.  There is no license fee for the SOFTWARE.  If Licensee
wishes to receive the SOFTWARE on media, there may be a small charge
for the media and for shipping and handling.  Licensee is
responsible for any and all taxes.

5. TERMINATION.  Without prejudice to any other rights, Licensor
may terminate this Agreement if Licensee breaches any of its terms
and conditions.  Upon termination, Licensee shall destroy all
copies of the SOFTWARE.

6. PROPRIETARY RIGHTS.  Title, ownership rights, and intellectual
property rights in the Product shall remain with RUTGERS.  Licensee 
acknowledges such ownership and intellectual property rights and will 
not take any action to jeopardize, limit or interfere in any manner 
with RUTGERS' ownership of or rights with respect to the SOFTWARE.  
The SOFTWARE is protected by copyright and other intellectual 
property laws and by international treaties.  Title and related 
rights in the content accessed through the SOFTWARE is the property 
of the applicable content owner and is protected by applicable law.  
The license granted under this Agreement gives Licensee no rights to such
content.

7. DISCLAIMER OF WARRANTY.  THE SOFTWARE IS PROVIDED FREE OF 
CHARGE, AND, THEREFORE, ON AN "AS IS" BASIS, WITHOUT WARRANTY OF 
ANY KIND, INCLUDING WITHOUT LIMITATION THE WARRANTIES THAT IT 
IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 
OR NON-INFRINGING.  THE ENTIRE RISK AS TO THE QUALITY AND 
PERFORMANCE OF THE SOFTWARE IS BORNE BY LICENSEE.  SHOULD THE 
SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, THE LICENSEE AND NOT 
LICENSOR ASSUMES THE ENTIRE COST OF ANY SERVICE AND REPAIR.  
THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF 
THIS AGREEMENT.  NO USE OF THE PRODUCT IS AUTHORIZED HEREUNDER 
EXCEPT UNDER THIS DISCLAIMER.

8. LIMITATION OF LIABILITY.  TO THE MAXIMUM EXTENT PERMITTED BY
APPLICABLE LAW,  IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY 
INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 
OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, INCLUDING, 
WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK 
STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL 
OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF ADVISED OF THE
POSSIBILITY THEREOF. 
*/
/* **************************************************************** *

  PURPOSE:      This file contains the functions for in-memory file 
                Input and Output Operations

 * **************************************************************** */

#include "FileIo.h"


const int FileIo::FILE_READ_MODE   =  'r';
const int FileIo::FILE_WRITE_MODE  =  'w';
const int FileIo::FILE_APPEND_MODE =  'a';

 FileIo::FileIo()
// ---------------------------------------------------------------------
//   FileIo(): contructor initializes
// --------------------------------------------------------------------- 
{
  _inMemoryOffset = 0;
  _fp = NULL;
  _curLineNo = 0;
  _totalLineNo = 0;
  _modificationTime = 0;
  _fileType = 0;
  _fileName = new CifString;
  _fileBuf = new CifString;
}
 FileIo::FileIo(const FileIo &inFile, char inType)
// ---------------------------------------------------------------------
//   FileIo(): Copy contructor initializes
// --------------------------------------------------------------------- 
{
  char type[2];

  _inMemoryOffset = inFile._inMemoryOffset;
  memset(type, 0, 2);
  if (inType == 0) _fileType = inFile._fileType;
  else             _fileType = inType;
  sprintf(type, "%c\0", _fileType);

  // Get a new file descriptor, in case of different type
  if (_fileType == FILE_READ_MODE ) 
    _fp = fopen(inFile._fileName->Text(), type);
  else _fp = NULL;

  _curLineNo = inFile._curLineNo;
  _totalLineNo = inFile._totalLineNo;
  _modificationTime = inFile._modificationTime;
  _fileType = inFile._fileType;

  // _fileBuf and _fileName still use the same buffer to make sure identical
  _fileBuf = inFile._fileBuf;
  _fileName = inFile._fileName;
}

 FileIo::~FileIo()
// ---------------------------------------------------------------------
//   ~FileIo(): destructor 
// --------------------------------------------------------------------- 
{
  _inMemoryOffset = 0;
  _fileType = 0;

  // CloseFile() was not called before the destructor was called
  if (_fp != NULL) fclose(_fp);
  _fp = NULL;
  _curLineNo = 0;
  _totalLineNo = 0;
  _modificationTime = 0;
  delete _fileName;
  delete _fileBuf;
}

int FileIo:: OpenFile(const char *fname, const char type) 
// ---------------------------------------------------------------------
//   OpenFile(): Open file according to input type
//       if the type is 'r' read in the whole file into file buffer.
//   Parameter: const char *fname-filename
//              const char type: 'r' -read, 'w'-write, 'a'-append
//   Return Values: FILE_OPERATION_TYPE_DISALLOW  
//                  FILE_OPERATION_PERMISSION_DENIED   
//                  FILE_NOT_EXIST                    
//                  FILE_OPERATION_SUCCEED            
// --------------------------------------------------------------------- 
{

  struct stat  fileStat;
  char *tmpFile;
  int filePermission;

  // Check File Permission

  if (fname == NULL) return  FILE_NOT_EXIST ;
  else if ((filePermission = FilePermission(fname, type)) < FILE_OPERATION_SUCCEED )
  return filePermission;

  _fp = NULL;
  _inMemoryOffset = 0;
  _curLineNo = 1;
  _fileName->Copy(fname);
    
  // Type : read 
  if (type == 'R' || type == 'r') {
    _fileType = FILE_READ_MODE;
    if (stat(fname, &fileStat) == 0) {
      _fp = fopen(fname, "r");
      if (_fp != NULL) {
	// Read file into _fileBuf 
	tmpFile = new char [fileStat.st_size+1];
	fread(tmpFile, sizeof(char), (int) fileStat.st_size,_fp);
	tmpFile[fileStat.st_size] = 0;
	_fileBuf->Copy(tmpFile);
	delete [] tmpFile ;

	// Calculated line number 
	_totalLineNo = GetTotalLineNo();

	// Get last modified time of the input file
	  _modificationTime = fileStat.st_mtime;
	return  FILE_OPERATION_SUCCEED;
      }
    }
    else return FILE_NOT_EXIST;
  }
  // Type: append 
  else if ( type == 'a' || type == 'A') 
    _fileType = FILE_APPEND_MODE;

  // Type: write
  else if ( type == 'W' || type == 'w') 
    _fileType = FILE_WRITE_MODE;

  if (stat(fname, &fileStat) == 0) 
    _modificationTime = fileStat.st_mtime;
  return FILE_OPERATION_SUCCEED;

}

int FileIo:: CloseFile()
// ---------------------------------------------------------------------
//   CloseFile(): Closes file 
//             if the _fileType is write or append, write to file
//   Return Value: SUCCEED or FAILURE
// --------------------------------------------------------------------- 
{
  struct stat curStat;

  // Opens file for writing
  if (_fileType == FILE_APPEND_MODE)
    _fp = fopen(_fileName->Text(), "a");
  else if (_fileType == FILE_WRITE_MODE)
    _fp = fopen(_fileName->Text(), "w");

  // Opens not succeed 
  if (_fp == NULL) return FAILURE;

  // Writes to file
  if (_fileType != FILE_READ_MODE)
    fwrite(_fileBuf->Text(), sizeof(char), _fileBuf->Length(),_fp);

  fclose(_fp);
  _fp = NULL;

  // Modifies last modified time
  if (_fileType != FILE_READ_MODE) {
    stat(_fileName->Text(), &curStat);
    _modificationTime = curStat.st_mtime;
  }
  return SUCCEED;
}

int FileIo::CountCurrLineChar()
// ---------------------------------------------------------------------
//   CountCurrLineChar(): Counts number of characters of current line
//   Return value: number of character of the current line
// --------------------------------------------------------------------- 
{
  long begLine, i;
  int count;
  count = 0;

  if (_inMemoryOffset >= _fileBuf->DimLength() || _inMemoryOffset <=0) return 0;

  // Finds the position of the beginning of line
  for (begLine=_inMemoryOffset; 
       _fileBuf->Text()[begLine] != '\n' && begLine>=0; begLine--);

  // Counts the number of spaces in the line
  if (begLine != _inMemoryOffset) 
    for (i= begLine+1; i<_inMemoryOffset; i++) {
      if (_fileBuf->Text()[i] == '\t') count += (8-count%8);
      else count ++;
    }
  return (count);
}


int  FileIo::Rewind()
// ---------------------------------------------------------------------
//   rewind(): set the _inMemoryOffset to the beginning of file
//   Return Value: SUCCEED or FAILURE
// --------------------------------------------------------------------- 
{
  // File opened failed or file was not opened
  _inMemoryOffset = 0;
  _curLineNo = 1;
  return SUCCEED;
}

int FileIo::Seek(long offset, const int whence)
// ---------------------------------------------------------------------
//   Seek(): moves _inMemoryOffset and reset _curLineNo
//      offset: the distance to move
//      whence: 0- from the beginning of file
//              1- from the current position of _inMemoryOffset
//              2- from the end of file
//   Return Value: number of characters scaned
// --------------------------------------------------------------------- 
{
  long i;

  // From beginning of file
  if (whence == 0) {
    _inMemoryOffset = offset;

    // Make sure _inMemoryOffset is in range
    if (_inMemoryOffset >= _fileBuf->Length()) 
      _inMemoryOffset = (long)_fileBuf->Length()-1;
    else if (_inMemoryOffset < 0) _inMemoryOffset = 0;
    
    // Finds current line number
    _curLineNo = 1;
    for (i=0; i< _inMemoryOffset; i++)
      if (_fileBuf->Text()[i] == '\n') _curLineNo ++;
  }
  // From end of file
  else if (whence == 2) {
    _inMemoryOffset = (long) _fileBuf->Length()-1 -offset;

    // Make sure _inMemoryOffset is in range
    if (_inMemoryOffset >= _fileBuf->Length()) 
      _inMemoryOffset = (long) _fileBuf->Length()-1;
    else if (_inMemoryOffset < 0) _inMemoryOffset = 0;

    // Finds current line number
    _curLineNo = _totalLineNo;
    for (i=(long)_fileBuf->Length()-1; i>= _inMemoryOffset; i--)
      if (_fileBuf->Text()[i] == '\n') _curLineNo --;
  }
  // From current position
  else {
    // Make sure _inMemoryOffset is in range
    if (offset + _inMemoryOffset < 0) 
      offset = -1 * _inMemoryOffset;
    else if (offset + _inMemoryOffset > _fileBuf->Length()) 
      offset = (long)_fileBuf->Length()- _inMemoryOffset -1;

    // Finds current line number
    if (offset > 0) {
      for (i=_inMemoryOffset; i<offset+_inMemoryOffset; i++) 
	if (_fileBuf->Text()[i] == '\n') 
	  _curLineNo ++;
    }
    else if (offset < 0) {
      for (i=_inMemoryOffset; i>offset+_inMemoryOffset; i--) {
	if (_fileBuf->Text()[i-1] == '\n') 
	  _curLineNo --;
      }
    }
    _inMemoryOffset += offset;
  }

  return offset;
}


int FileIo::GetChar()
// ---------------------------------------------------------------------
//   GetChar(): Get one word from the current _inMemoryOffset position of
//              file and move the _inMemoryOffset is one position ahead 
//   return value: EOF for failure or 
//                 the character in the current position of _inMemoryOffset
// --------------------------------------------------------------------- 
{
  int in;

  // _inMemoryOffset is out of range
  if (_fileBuf->Length() <= _inMemoryOffset) return(EOF);
  // Get character
  in = _fileBuf->Text()[_inMemoryOffset];
  if (in == '\n') _curLineNo++;
  _inMemoryOffset ++;
  return(in);
}

CifString* FileIo::GetWord()
// ----------------------------------------------------------------------
//   GetWord(): Get one word from the current _inMemoryOffset position of
//              file and the _inMemoryOffset also move ahead
//   return value: NULL for failure, 
//                 or the word from the current position
// --------------------------------------------------------------------- 
{
  long beginword;
  int in;
  CifString *newstring;

  // _inMemoryOffset is out of range
  if (_fileBuf->Length() <=_inMemoryOffset) return(NULL);

  // scans through the spaces before the word
  do {
    in = GetChar();
  } while (in != EOF && (in == ' ' || in == '\n' || in == '\t'));

  // Starts the word
  beginword = _inMemoryOffset;
  do {
    in = GetChar();
  }while (in != EOF  && in != ' ' && in != '\n' && in!= '\t') ;

  // put in the word to newword buffer
  if (_inMemoryOffset - beginword > 0) {
    newstring = new CifString;
    newstring->CopyN(&_fileBuf->Text()[beginword], 
		     (int) (_inMemoryOffset-beginword));
    return(newstring);
  }
  else return NULL;
}

CifString* FileIo::GetLine()
// ---------------------------------------------------------------------
//   GetLine(): Get one line from the current _inMemoryOffset position of
//              file and the _inMemoryOffset also move ahead
//   return value: NULL for failure, 
//                 or the line from the current position
// --------------------------------------------------------------------- 
{
  long begLine;
  int in;
  CifString *newString;

  // _inMemoryOffset is out of range
  if (_fileBuf->Length() <=_inMemoryOffset) return(NULL);

  begLine = _inMemoryOffset;
  
  // gets the line
  do {
    in = GetChar();
  }while (in != EOF && in != '\n');

  // puts the line into buffer
  if (_inMemoryOffset - begLine > 0) {
    newString = new CifString;
    newString->CopyN(&_fileBuf->Text()[(int) begLine],
		     (int) (_inMemoryOffset -begLine));
    return(newString);
  }
  else return NULL;
}

int FileIo::ScanF(const char *fmt, ...)
// ---------------------------------------------------------------------
//   ScanF(): performs the same as scanf from stdio.h
//   Parameter: const char *fmt: Format for the following parameters
//            ... buffer variables to put scaned values
//   return value: matched number
// --------------------------------------------------------------------- 
{
  int i, dotnotfound, in;
  int  *ival, nmatched;
  long *lival;
  double *dval;
  const char *p;
  char *sval;
  CifString *Sval;
  va_list ap;

  // _inMemoryOffset is out of range or fmt is not given
  if (_fileBuf->Length() <=_inMemoryOffset || fmt == NULL) return 0;
  va_start(ap, fmt);

  // Calculate number of input arguments
  nmatched = 0;
  for (p=fmt; *p; p++) {
    if (*p != '%') {
      if (_fileBuf->Text()[_inMemoryOffset] == *p && _inMemoryOffset <_fileBuf->Length())
	  _inMemoryOffset ++;
      continue;
    }

    // scans through the spaces
    for (; _inMemoryOffset < _fileBuf->Length() &&
	 ( _fileBuf->Text()[_inMemoryOffset] == '\n' ||
	   _fileBuf->Text()[_inMemoryOffset] == ' ' ||
	   _fileBuf->Text()[_inMemoryOffset] == '\t'); _inMemoryOffset++);
    if (_inMemoryOffset >= _fileBuf->Length())
      break;

    // Pointer p forward
    p++;
    // Character
    if (strncmp("c", p,1) ==0) {
      // Get next unknown argument
      sval = va_arg(ap, char *);

      nmatched++;
      in = GetChar();
      if (in != EOF)
	*sval = (char) in;
      else break;
    }
    // Long
    else if (strncmp("ld", p,2) ==0) {
      // Get next unknown argument
      lival = va_arg(ap, long *);

      nmatched++;
      *lival = atoi(&_fileBuf->Text()[_inMemoryOffset]);
      while (_fileBuf->Length() > _inMemoryOffset &&
	     isdigit(_fileBuf->Text()[_inMemoryOffset])) _inMemoryOffset ++;

      // ld is two letters
      p++;
    }
    // Ingeger:
    else if (strncmp("d", p,1) ==0) {
      // Get next unknown argument
      ival = va_arg(ap, int *);

      nmatched++;
      *ival = atoi(&_fileBuf->Text()[_inMemoryOffset]);

      // correct the position of _inMemoryOffset
      while (_fileBuf->Length() > _inMemoryOffset &&
	     isdigit(_fileBuf->Text()[_inMemoryOffset])) _inMemoryOffset ++;
    }
    // Double:
    else if (strncmp("f", p,1) ==0 || strncmp("lf", p, 2) ==0 ) {
      // Get next unknown argument
      dval = va_arg(ap, double *);

      *dval = atof(&_fileBuf->Text()[_inMemoryOffset]);

      // correct the position of _inMemoryOffset
      dotnotfound = TRUE;
      while (_fileBuf->Length() > _inMemoryOffset &&
	     (isdigit(_fileBuf->Text()[_inMemoryOffset]) || 
	     _fileBuf->Text()[_inMemoryOffset] == '.' && dotnotfound)) {
	if (_fileBuf->Text()[_inMemoryOffset] == '.') dotnotfound = FALSE;
	_inMemoryOffset ++;
      }
      nmatched++;
      // lf is two letters
      if (strncmp("lf", p, 2) ==0) p++;
    }
    // Character CifString:
    else if (strncmp("s", p,1) ==0 || strncmp("S", p,1) ==0) {
      // Get next unknown argument
      if (strncmp("s", p,1) ==0)
	  sval = va_arg(ap, char*); 
      else if (strncmp("S", p,1) ==0) 
	Sval = va_arg(ap, CifString *); 

      // Get the string until meet spaces
      for (i=0; _inMemoryOffset < _fileBuf->Length() &&
	   _fileBuf->Text()[_inMemoryOffset] != '\n' && 
	   _fileBuf->Text()[_inMemoryOffset] != ' ' && 
	   _fileBuf->Text()[_inMemoryOffset] != '\t'
	   ; i++) {
	in = GetChar();
	if (in != EOF && strncmp("s", p, 1) ==0) 
	  sval[i] = (char) in;
	else if (in != EOF && strncmp("S", p, 1) ==0) {
	  if (Sval->DimLength() <=i)
	    Sval->Grow();
	  (*Sval)+= (char) in; //->Fill((char) in, i, 1);
	}
	else break;
      }
      if (strncmp("S", p, 1) ==0)  (*Sval)+= (char) NULL;//->Fill( (char) NULL, i, 1);
      else sval[i] = (char) NULL;

      nmatched++;
    }
  }
  va_end(ap);
  return(nmatched);
}


int FileIo::PutChar(const char inchar)
// ---------------------------------------------------------------------
//   PutChar(): performs the same as putchar from stdio.h
//   Parameter: char inchar, the input character
//   Return value: Failure or the input character
// --------------------------------------------------------------------- 
{
  // The dimemtion is not enough to hold the data, grow twice as much
  if (_inMemoryOffset >= _fileBuf->DimLength() -1) 
    if (!_fileBuf->GrowBy(_fileBuf->DimLength())) return(EOF);
  _fileBuf->Fill(inchar, (int) _inMemoryOffset, 1);
  if (inchar == '\n') _curLineNo ++;
  _inMemoryOffset++;
  return(inchar);
}

int FileIo::PutLine(const char* inchar)
// ---------------------------------------------------------------------
//   PutLine(): Put inchar into _fileBuf and a newline at the end of the line
//              if the last character is not newline
//   Return value: FAILURE or SUCCEED
// --------------------------------------------------------------------- 
{
  int strLen, i, in;

  strLen = strlen(inchar);
  for (i=0; i<strLen; i++) 
    in = PutChar(inchar[i]);
  // If the last character is not newline, put newline in
  if (inchar[strLen-1] != '\n') in = PutChar('\n');
  if (in == FAILURE) return in;
  else return SUCCEED;
}
int FileIo::PutLine(const CifString inchar)
// ---------------------------------------------------------------------
//   PutLine(): Put inchar into _fileBuf and a newline at the end of the line
//              if the last character is not newline
//   Return value: FAILURE or SUCCEED
// --------------------------------------------------------------------- 
{
  return (PutLine(inchar.Text()));
}

int FileIo::PutWord(const char* inchar)
// ---------------------------------------------------------------------
//   PutWord(): Put inchar into _fileBuf and a space at the begin of line
//   Return value: FAILURE or SUCCEED
// --------------------------------------------------------------------- */
{
  int strLen, i, in;

  strLen = strlen(inchar);
  for (i=0; i<strLen; i++) 
    in = PutChar(inchar[i]);
  if (in == FAILURE) return in;
  else return SUCCEED;
}
int FileIo::PutWord(const CifString inchar)
// ---------------------------------------------------------------------
//   PutWord(): Put inchar into _fileBuf and a space at the beginning of line
//   Return value: FAILURE or SUCCEED
// --------------------------------------------------------------------- */
{
  return (PutWord(inchar.Text()));
}

int FileIo::PrintF(const char *fmt ...)
// ---------------------------------------------------------------------
//   PrintF(): performs the same as printf from stdio.h
//   Parameter: const char *fmt: Format for the following parameters
//            ... variables to print
//   return value: Failure or number of characters was printed
// --------------------------------------------------------------------- 
{
  int  i, nchar, cval, ret;
  long ival;
  char tmpString[100];
//  CifString Sval;
  double dval;
  const char *p;
  char *sval;

  va_list ap;
  va_start(ap, fmt);

  if (fmt == NULL) return(FAILURE);

  // Calculate number of input arguments
  nchar = 0;
  ret = 1;
  for (p=fmt; *p && ret; p++) {

    if (*p != '%') {
      ret = PutChar(*p);
      continue;
    }
    // pointer forward
    ++p;
    // for ld or lf pointer p forward
    if (*p == 'l' && (*(p+1) == 'd' ||  *(p+1) == 'f')) ++p;
    switch(*p) {

      // Character:
    case 'c':
      cval = va_arg(ap, int);
      ret = PutChar(cval);
      if (ret)	nchar++;
      break;

      // Integer:
    case 'd':
      ival = va_arg(ap, long);
      memset(tmpString, 0, 100);
      sprintf(tmpString,"%d", ival);
      for (i=0; i<strlen(tmpString) && ret; i++, nchar ++) 
	ret = PutChar(tmpString[i]);
      break;

      // Double:
    case 'f':
      dval = va_arg(ap, double);
      memset(tmpString, 0, 100);
      sprintf(tmpString,"%lf", dval);
      for (i=0; i<strlen(tmpString) && ret; i++, nchar ++) 
	ret = PutChar(tmpString[i]);
      break;
      
      // Character CifString: char *
    case 's':
      for (sval = va_arg(ap, char*);*sval && ret; sval++)
	ret = PutChar(*sval); 
      break;

      // Character CifString: CifString 
/*
    case 'S':
      Sval = va_arg(ap, CifString);
      for (i=0; i<Sval.Length()&& ret; i++)
	ret = PutChar(Sval[i]); 
      break;
*/
      // Others
    default:
      ret = PutChar(*p);
      break;
    }
  }
  va_end(ap);
  return(nchar);
}


long FileIo::GetInMemoryOffset()
// ---------------------------------------------------------------------
//   GetInMemoryOffset(): gets the value of _inMemoryOffset
//   Return Values: _inMemoryOffset
// --------------------------------------------------------------------- 
{
  return(_inMemoryOffset);
}

CifString *FileIo::GetFileName()
// ---------------------------------------------------------------------
//   GetFileName(): gets the value of filename
//   Return Values: filename
// --------------------------------------------------------------------- 
{
  return(_fileName);
}

time_t FileIo::GetModifiedTime()
// ---------------------------------------------------------------------
//   GetModifiedTime(): gets the value of _modificationTime
//   Return Values: _modificationTime
// --------------------------------------------------------------------- 
{
  return(_modificationTime);
}
int FileIo::GetCurrLineNo()
// ---------------------------------------------------------------------
//   GetCurrLineNo(): gets the value of current line number
//   Return Values: _curLineNo
// --------------------------------------------------------------------- 
{
  return(_curLineNo);
}

int FileIo::GetTotalLineNo()
// ---------------------------------------------------------------------
//   GetTotalLineNo(): gets the value of totalline number
//   Return Values: _totalLineNo
// --------------------------------------------------------------------- 
{
  _totalLineNo = _fileBuf->LineCount();
  return(_totalLineNo);
}

char FileIo::GetFileType()
// ---------------------------------------------------------------------
//   GetFileType(): gets the value of _fileType
//   Return Values: _fileType
// --------------------------------------------------------------------- 
{
  return(_fileType);
}


// *********************************************************************
// 
//    The following procedure does not belongs to the class FileIo, 
//    but it is still doing file operation
//
// **********************************************************************

int FilePermission(const char *fname, const char type)
// ---------------------------------------------------------------------
//   FilePermission(): Checks File permission
//   Parameter: const char *fname  - filename
//              const char type: 'r' -read, 'w'-write, 'a'-append
//   Return Values: FILE_OPERATION_TYPE_DISALLOW  
//                  FILE_OPERATION_PERMISSION_DENIED   
//                  FILE_NOT_EXIST                    
//                  FILE_OPERATION_SUCCEED            
// --------------------------------------------------------------------- 
{
  struct stat dirStat, fileStat;
  int i, group;
  int ngrps;
  uid_t uid;
  gid_t gids[10];

  // Only read, write and append types are allowed for file operations
  if (type != 'R' && type != 'r' && type != 'A' && type != 'a' && 
      type != 'W' && type != 'w') 
    return FILE_OPERATION_TYPE_DISALLOW  ;

  // get directory information
 if (stat(".", &dirStat)==0) {

   // Get group ids of the user
    uid = getuid();
    ngrps = getgroups(10, gids);

    if (type == 'R' || type == 'r') {
      // Check the file status 
      if (stat(fname, &fileStat) == 0) {

	// user or world permission allowed
	if ((fileStat.st_mode & '\4') == '\4' ||
	    uid == fileStat.st_uid && 
	    (fileStat.st_mode & ('\40' * '\10')) == '\40' * '\10')
	  return FILE_OPERATION_SUCCEED ;

	// Check group permission
	else {
	  group = 0;
	  for (i=0; i<ngrps; i++) {
	    if (gids[i] == fileStat.st_gid){
	      group = 1;
	      break;
	    }
	  }
	  if (group && (fileStat.st_mode & '\040') == '\40')
	    return FILE_OPERATION_SUCCEED ;
	  else return FILE_OPERATION_PERMISSION_DENIED;
	}
      }
      else return FILE_NOT_EXIST;
    }
    else {
      // Check the file status 
      if (stat(fname, &fileStat) == 0) {

	// user or world permission allowed
	if ((fileStat.st_mode & '\2') == '\2'||
	    uid == fileStat.st_uid && (fileStat.st_mode &'\200')== '\200')
	  return FILE_OPERATION_SUCCEED  ;

	// Check group permission
	else {
	  group = 0;
	  for (i=0; i<ngrps; i++) {
	    if (gids[i] == fileStat.st_gid) {
	      group = 1;
	      break;
	    }
	  }
	  if (group && (fileStat.st_mode & '\20') == '\20')
	    return FILE_OPERATION_SUCCEED  ;
	  else return  FILE_OPERATION_PERMISSION_DENIED;
	}
      }

      // File does not exist, check directory status 
      else {

	// user or world permission allowed
	if ((dirStat.st_mode & '\2') == '\2'||
	    uid == dirStat.st_uid && (dirStat.st_mode &'\200')== '\200')
	  return FILE_OPERATION_SUCCEED;

	// Check group permission
	else {
	  group = 0;
	  for (i=0; i<ngrps; i++) {
	    if (gids[i] == dirStat.st_gid) {
	      group = 1;
	      break;
	    }
	  }
	  if (group && (dirStat.st_mode & '\20') == '\20')
	    return FILE_OPERATION_SUCCEED;
	  else return  FILE_OPERATION_PERMISSION_DENIED;
	}
      }
    }
  }
  return FILE_OPERATION_SUCCEED;
}


int FilePermission(FileIo infile, char type)
// ---------------------------------------------------------------------
//   FilePermission(): Checks File permission
//   Parameter: FileIo: infile
//              const char type: 'r' -read, 'w'-write, 'a'-append
//   Return Values: FILE_OPERATION_TYPE_DISALLOW  
//                  FILE_OPERATION_PERMISSION_DENIED   
//                  FILE_NOT_EXIST                    
//                  FILE_OPERATION_SUCCEED            
// --------------------------------------------------------------------- 
{
  if (type == 0) 
    return(FilePermission(infile.GetFileName()->Text(), infile.GetFileType()));
  else
    return(FilePermission(infile.GetFileName()->Text(), type));
}
