//------------------------------------------------------------------------------
// module fileutil.cpp version 2.0b (english) //
// //
// utility functions to read values/strings from a stream. //
// //
// copyright (c) 1998-2003 by Lars Haendel //
// home: www.newty.de //
// //
// This program is free software and can be used under the terms of the //
// GNU licence. See header file for further information and disclaimer. //
// //
//------------------------------------------------------------------------------
#include <stdio> // due to: sprintf()
#include "fileutil.h" //
#include "util.h"
#include "defines.h" // STS (standard string length) and IsComment()
#define LEN_ILLEGAL_SEQ 10
//----------------------------------------------------------------------------------------------------------------------
// return flag (bool) as string 'true' or 'false'
const char* FlagToString(const int& flag, const bool& f_LeftAlign/*=false*/)
{
if(flag<0 || flag>1)
return "??";
else
if(flag)
if(f_LeftAlign)
return "true "; // add space at the end
else
return "true";
else
return "false";
}
//----------------------------------------------------------------------------------------------------------------------
// size of a string excluding terminating '\0', i.e. szString[size] is '\0'
int SizeOfString(const char*const& szString)
{
int size =0;
while(szString[size]!='\0')
size++;
return size;
}
//----------------------------------------------------------------------------------------------------------------------
// remove a filename's extension
void RemoveExt(char*const& szFilename)
{
int len = SizeOfString(szFilename); // determine length
while(len>=0) // position back to last '.'
{
if(szFilename[len]=='.') // if point is found
{
szFilename[len] = '\0'; // 'delete' rest of string
break; // and finish
}
len--;
}
}
//----------------------------------------------------------------------------------------------------------------------
// check if passed string is a valid filename without(!) path and extension
bool CheckName(const char*const& szName)
{
int i=0;
while(szName[i]!='\0')
{
if(szName[i]=='\\'|| szName[i]=='/' || szName[i]==':' || szName[i]=='.') // no backslash, slash, colon or dot
return false;
i++;
}
return true; // all ok
}
//----------------------------------------------------------------------------------------------------------------------
// exchange a filename's extension
void ExchangeExt(char*const& szFilename, const char*const& szExtension)
{
RemoveExt(szFilename); // remove extension
strcat(szFilename, "."); // append dot
strcat(szFilename, szExtension); // append extension
}
//----------------------------------------------------------------------------------------------------------------------
// a path delimiter is appended if passed string is not empty and if it is not alread there
void EnsurePathDelimiter(char*const& szPath)
{
int len = SizeOfString(szPath); // determine length
if(len>0) // if string is not empty
if(szPath[len-1]!=PATH_DELIMITER) // append path delimiter if necessary
{
szPath[len] = PATH_DELIMITER; // append and ...
szPath[len+1] = '\0'; // ... terminate string
}
}
//----------------------------------------------------------------------------------------------------------------------
// copy path without filename to <szPath>
void ExtractPath(char*const& szPath, const char*const& szFilename)
{
szPath[0]='\0'; // 'delete' path
int i=0, pos=0;
while(szFilename[i++]!='\0') // find last path delimiter, set <pos> to point to next character
if(szFilename[i-1]==PATH_DELIMITER) // path delimiter found
pos=i; // store position
for(int i=0;i<pos;i++) // copy path (<pos> will be 0 if no delimiter was found)
szPath[i]=szFilename[i];
szPath[pos]='\0';
}
//----------------------------------------------------------------------------------------------------------------------
// replaces each wrong path delimiter (COMPL_PATH_DELIMITER) by the correct one (PATH_DELIMITER)
void CorrectPathDelimiter(char*const& szFilename)
{
int i=0;
while(szFilename[i]!='\0') // parse whole string
{
if(szFilename[i]==COMPL_PATH_DELIMITER)
szFilename[i]=PATH_DELIMITER; // replace
i++;
}
}
//----------------------------------------------------------------------------------------------------------------------
// prepend path to filename if necessary, i.e. if filename is relative (does not contain a ':') and if filename is
// not empty
void PrefixPath(char*const& szFilename, const char*const& szDir)
{
if(szFilename[0]=='\0') // return if filename is empty
return;
int i=0;
while(szFilename[i++]!='\0') // return if filename contains ':' which indicates that it is already a full ...
if(szFilename[i-1]==':') // ... qualified name
return;
char szText[STS]; // prefix filename
strcpy(szText, szDir);
EnsurePathDelimiter(szText); // ensure path delimiter
strcat(szText, szFilename);
strcpy(szFilename, szText);
}
//----------------------------------------------------------------------------------------------------------------------
// get pointer to full qualified filename using PrefixPath()
const char* GetPrefixedPath(const char*const& szFileName, const char*const& szDir)
{
static char szText[STS];
strcpy(szText, szFileName); // copy
PrefixPath(szText, szDir); // prepend path if necessary
return szText;
}
//----------------------------------------------------------------------------------------------------------------------
// return pointer to filename without preceding path
const char* GetFilename(const char*const& szPath)
{
int len = SizeOfString(szPath); // get string length
while(szPath[len]!=PATH_DELIMITER && szPath[len]!=':' && len >=0) // position back from the end to the first ...
len--; // ... path delimiter or ':' (path on floppy)
return &szPath[len+1];
}
//----------------------------------------------------------------------------------------------------------------------
// search stream for string 'szKey' followed by a '=' and read following string.
bool ReadKeyString(ifstream& file, const char*const& szKey, char* szString, const int& size, const int& maxLines)
{
szString[0]='\0'; // 'delete' string
file.clear();
streampos curPos = file.tellg(); // preserve actual stream position
try{
SearchKey(file, szKey, maxLines, '['); // search key
} catch(int errNo) { goto fail; }
if(file.get()!='=') // if next character is not '=' -> return false
goto fail;
if(skipws(file)!=0) // remove whitespaces
throw ValueMissing;
ReadString(file, szString, size); // read string
file.seekg(curPos); // restore stream position
return true;
fail:
file.seekg(curPos); // restore stream position
return false;
}
//----------------------------------------------------------------------------------------------------------------------
// search stream for string 'szKey' followed by a '=', read following string and compare it with
// 'true'/'false'.
int ReadKeyBool(ifstream& file, const char*const& szKey, const int defaultValue, const int& maxLines)
{
char szString[STS];
if(!ReadKeyString(file, szKey, szString, STS, maxLines)) // read 'szKey = string'
return defaultValue;
if(strcmp(szString, "true")==0) // if key was found: compare string to 'true' and 'false'
return true;
else
if(strcmp(szString, "false")==0)
return false;
else
throw BoolReadFailed;
}
//----------------------------------------------------------------------------------------------------------------------
// global variables
static char szKey [STS];
static char szErrCode[STS]; // error description
static char szIllSeq [STS]; // illegal sequence
static char illChar ='\0'; // illegal character
char& GetIllegalCharacter() { return illChar; }
const char* GetLastKey() { return szKey; };
//----------------------------------------------------------------------------------------------------------------------
// function to get a description of the last error.
const char* GetLastError(const int& errNo)
{
switch(errNo)
{
case ErrReadingBits : sprintf(szErrCode, "Encountered '%c' when reading bits! Only '0' and '1' are valid characters!"
, illChar); break;
case UnterminatedBits : sprintf(szErrCode, "Encountered '%c' where bitstring should be terminated by a whitespace!"
, illChar); break;
case BoolReadFailed : sprintf(szErrCode, "Key '%s' must be either 'true' or 'false'!", szKey); break;
case ValueMissing : sprintf(szErrCode, "Value missing for key '%s'!", szKey); break;
case NoWhitespace :
switch(illChar)
{
case '.' : strcpy(szErrCode,"I found a point directly after the value! \nMaybe you've specified a float\
value where an integer was expected ?"); break;
case '+' :
case '-' : strcpy(szErrCode,"'+' or '-' directly follows preceeding number!"); break;
default : sprintf(szErrCode,"Encountered illegal character '%c'!", illChar);
} break;
case KeyNotFound : sprintf(szErrCode,"Key '%s' not found!", szKey); break;
case UnexpectedEndl : strcpy(szErrCode,"Unexpected end of line!"); break;
case UnexpectedEOF : strcpy(szErrCode,"Unexpected end of file!"); break;
case IllegalCharacter :
if(illChar=='+' || illChar=='-')
sprintf(szErrCode,"Displaced character '%c' - maybe a blank to much?", illChar);
else
sprintf(szErrCode,"Encountered illegal character '%c'!", illChar);
break;
case NoEndlFound :
szIllSeq[LEN_ILLEGAL_SEQ]='\0'; // terminate the string
sprintf(szErrCode, "Line should be ended but I found '%s'!", szIllSeq); break;
default : strcpy(szErrCode,"Unknown reason!"); break;
}
return szErrCode;
}
//----------------------------------------------------------------------------------------------------------------------
// wrapper which removes all whitespaces from a stream. an exception is thrown if no endl is encountered
int skipwsEndl(ifstream& file)
{
int lines = skipws(file); // remove all whitespaces
if (lines==0)
{
file.get(szIllSeq, STS);
throw NoEndlFound; // throw exception if no endl occured
}
return lines;
}
//----------------------------------------------------------------------------------------------------------------------
// like skipwsEndl() but the function skipwsEx(), which addionally removes comments, is used
int skipwsExEndl(ifstream& file)
{
int lines = skipwsEx(file); // remove all whitespaces and(!) comments
if (lines==0)
{
file.get(szIllSeq, STS);
throw NoEndlFound; // throw exception if no endl occured
}
return lines;
}
//----------------------------------------------------------------------------------------------------------------------
// wrapper which removes all whitespaces from a stream. an exception is thrown if an endl is encountered
void skipwsNoEndl(ifstream& file)
{
int lines = skipws(file); // remove all whitespaces
if (lines>0)
throw UnexpectedEndl; // throw exception if an endl occured
}
//----------------------------------------------------------------------------------------------------------------------
// like skipwsNoEndl() but the function skipwsEx(), which addionally removes comments, is used
void skipwsExNoEndl(ifstream& file)
{
int lines = skipwsEx(file); // remove all whitespaces and(!) comments
if (lines>0)
throw UnexpectedEndl; // throw exception if an endl occured
}
//----------------------------------------------------------------------------------------------------------------------
// functions reads all whitespaces from an ifstream returning the number of encountered endl's
int skipws(ifstream& file)
{
file.clear(); // reset stream state
int line =0;
int c;
// remove whitespaces counting endl's
do
{
c=file.get();
if(!isspace(c))
break;
if(c =='\n') // if it's an endl: increment counter
line++;
} while (file.fail()==0); // proceed while there are ws in the stream
// finish
file.clear(); // reset stream state
file.putback((char) c); // put the last character back in the stream cause it's no ws
return line;
}
//----------------------------------------------------------------------------------------------------------------------
// functions reads all whitespaces and comments from an ifstream returning the number of encountered endl's.
int skipwsEx(ifstream& file)
{
file.clear(); // reset stream state
int line =0;
int c;
// while there are whitespaces or comments ...
do
{
c =file.get(); // read char and ...
if(IsComment((char) c)) // ... if it's a comment -> ...
{
file.ignore(INT_MAX, '\n'); // ... skip the rest of the line
line++; // increment line-counter
}
else
if (c =='\n') // else if it's an endl
line++; // increment line-counter
else
if(!isspace(c)) // else if it's neither ws nor comment -> break
break;
}
while (file.fail()==0);
// finish
file.clear(); // reset stream state
file.putback((char) c); // put the last character back in the stream
return line;
}
//----------------------------------------------------------------------------------------------------------------------
// utility-function for template readExpNoEndl() - is called to examine a stream failstate.
void ReadFailed(ifstream& file)
{
file.clear(); // reset stream state
int c;
if (EOF == (char) file.peek()) // peek and compare with EOF (end of file)
throw UnexpectedEOF;
// else: inspect if the character is a ws. if so it cannot be the one which caused the error. so go back
// in the stream until you find a none ws. this is the one the user wants to know;-)
while(isspace(c = file.get()) && file.fail()==0)
file.seekg(-2,ios::cur);
illChar = (char) c;
throw IllegalCharacter; // throw exception
}
//----------------------------------------------------------------------------------------------------------------------
// function reads a string from an ifstream. All whitespaces, comments and the delimiter
// terminate the string. The delimiter is left in the stream. The delimiter is returned.
// note: remind that a string can be empty.
char ReadString(ifstream& file, char* szString, const int& size, const char& delimiter)
{
file.clear(); // reset stream state
int i=0, c;
do
{
c = file.get(); // read character
if(file.fail()!=0) // if the stream gets into a fail state
{
file.clear(); // just for principle: reset stream state
throw UnexpectedEOF; // throw an exception. note: EOF is the only possible reason
}
// if character is whitespace, comment, delimiter or EOF -> stop reading, i.e. terminate the string and return
if(isspace(c) || IsComment((char) c) || c==delimiter || EOF == (char) c)
{
file.putback((char) c);
break;
}
szString[i++]= (char) c; // else: copy character to string and continue
} while(i<size-1);
szString[i]='\0'; // terminate string
return (char) c; // return delimiter
}
//----------------------------------------------------------------------------------------------------------------------
// function searches stream for string 'szKey'. The key must be at the begin of a line.
// if key is found, stream position is moved to first following non-whitespace
//
// exceptions are thrown if:
// a) key not found
// b) key found but first following non-whitespace is EOF
//
// return value: # encountered endl
int SearchKey(ifstream& file, const char*const& _szKey, const int& maxLines/*=INT_MAX*/, const char& delimiter/*=EOF*/)
{
if(maxLines<1)
throw 1;
file.clear();
int line =0;
char buffer[STS+5]; // note: don't know anymore if this is necessary
buffer[0] = '\0';
strcpy(szKey, _szKey); // store in global
// read linewise until match is found or EOF is encountered
while(strcmp(buffer, szKey)!=0 /* && file.fail()==0 */)
{
line += skipwsEx(file); // skip all whitespaces and comments
if(file.peek()=='=') // skip '=' cause ReadString() won't do it
file.get();
// key wasn't found if EOF is reached
if(file.peek()==delimiter || line>maxLines || ReadString(file, buffer, STS, '=')==EOF)
throw KeyNotFound;
}
line += skipwsEx(file); // position to next non-whitespace
if(file.fail()!=0) // note: only way to get into a fail-state is EOF
throw UnexpectedEOF;
return line;
}
//----------------------------------------------------------------------------------------------------------------------
// read 'nBits' bits from ifstream
void ReadBits(ifstream& file, int& value, const int& nBits/*=32*/)
{
file.clear(); // reset stream state
ClearAllBits(value); // clear all bits
int c;
for(int i=0;i<min(MaxBits, nBits);i++)
{
c=file.get(); // read character
if(c=='1')
SetBit(value,i); // set bit
else if(c!='0')
{
illChar = (char) c; // store illegal character
throw ErrReadingBits; // throw error if character is neither '0' nor '1'
}
}
// ensure following whitespace
if(!isspace(file.peek()))
{
illChar = (char) c; // store illegal character
throw UnterminatedBits;
}
}