Back

1     //------------------------------------------------------------------------------
2     // Module FileUtil.cpp                                                        //
3     //                                                                            //
4     //    Utility functions to read values/strings from a stream. You can         //
5     //    realize format-sensitive reading without the "always read a whoole      //
6     //    line" concept.                                                          //
7     //                                                                            //
8     //    copyright (c) 1998-2004 by Lars Haendel                                 //
9     //    home: www.newty.de                                                      //
10    //                                                                            //
11    //    This program is free software and can be used under the terms of the    //
12    //    GNU licence. See header file for further information and disclaimer.    //
13    //                                                                            //
14    //------------------------------------------------------------------------------
15
16
17    #include <stdio>                 // due to:        sprintf()
18
19    #include "FileUtil.h"            //
20
21    #define LEN_ILLEGAL_SEQ 10
22
23
24
25    //----------------------------------------------------------------------------------------------------------------------
26    // return flag (bool) as string 'true' or 'false'
27    const char* FlagToString(const int& flag, const bool& f_LeftAlign/*=false*/)
28    {
29       if(flag<0 || flag>1)
30          return "??";
31       else
32          if(flag)
33             if(f_LeftAlign)
34                return "true ";   // add space at the end
35             else
36                return "true";
37          else
38             return "false";
39    }
40
41
42
43    //----------------------------------------------------------------------------------------------------------------------
44    // global variables
45    static char szKey  [STS];
46    static char szErrCode[STS];         // error description
47    static char szIllSeq [STS];         // illegal sequence
48    static char cIllChar  ='\0';        // illegal character
49
50    char& GetIllegalCharacter() { return cIllChar; }
51    const char* GetLastKey() { return szKey; };
52
53
54    //----------------------------------------------------------------------------------------------------------------------
55    // function to get a description of the last error.
56    const char* GetLastError(const int& errNo)
57    {
58       switch(errNo)
59       {
60          case ErrReadingBits : sprintf(szErrCode, "Encountered '%c' when reading bits! Only '0' and '1' are valid characters!"
61                                           , cIllChar); break;
62          case UnterminatedBits : sprintf(szErrCode, "Encountered '%c' where bitstring should be terminated by a whitespace!"
63                                           , cIllChar); break;
64          case BoolReadFailed : sprintf(szErrCode, "Key '%s' must be either 'true' or 'false'!", szKey); break;
65          case ValueMissing : sprintf(szErrCode, "Value missing for key '%s'!", szKey); break;
66          case NoWhitespace :
67             switch(cIllChar)
68             {
69                case '.' : strcpy(szErrCode,"I found a point directly after the value! \nMaybe you've specified a float\
70    value where an integer was expected ?"); break;
71                case '+' :
72                case '-' : strcpy(szErrCode,"'+' or '-' directly follows preceeding number!"); break;
73                default  : sprintf(szErrCode,"Encountered illegal character '%c'!", cIllChar);
74             }  break;
75
76          case KeyNotFound    : sprintf(szErrCode,"Key '%s' not found!", szKey); break;
77          case UnexpectedEndl : strcpy(szErrCode,"Unexpected end of line!"); break;
78          case UnexpectedEOF  : strcpy(szErrCode,"Unexpected end of file!"); break;
79          case IllegalCharacter :
80             if(cIllChar=='+' || cIllChar=='-')
81                sprintf(szErrCode,"Displaced character '%c' - maybe a blank to much?", cIllChar);
82             else
83                sprintf(szErrCode,"Encountered illegal character '%c'!", cIllChar);
84             break;
85
86          case NoEndlFound :
87             szIllSeq[LEN_ILLEGAL_SEQ]='\0';                                                  // terminate the string
88             sprintf(szErrCode, "Line should be ended but I found '%s'!", szIllSeq); break;
89          default : strcpy(szErrCode,"Unknown reason!"); break;
90       }
91
92       return szErrCode;
93    }
94
95
96    //----------------------------------------------------------------------------------------------------------------------
97    // wrapper which removes all whitespaces from a stream. an exception is thrown if no endl is encountered
98    int skipwsEndl(ifstream& file)
99    {
100      int lines = skipws(file);              // remove all whitespaces
101
102      if (lines==0)
103      {
104         file.get(szIllSeq, STS);
105         throw NoEndlFound;                  // throw exception if no endl occured
106      }
107
108      return lines;
109   }
110
111
112   //----------------------------------------------------------------------------------------------------------------------
113   // like skipwsEndl() but the function skipwsEx(), which addionally removes comments, is used
114   int skipwsExEndl(ifstream& file)
115   {
116      int lines = skipwsEx(file);         // remove all whitespaces and(!) comments
117
118      if (lines==0)
119      {
120         file.get(szIllSeq, STS);
121         throw NoEndlFound;               // throw exception if no endl occured
122      }
123
124      return lines;
125   }
126
127
128   //----------------------------------------------------------------------------------------------------------------------
129   // wrapper which removes all whitespaces from a stream. an exception is thrown if an endl is encountered
130   void skipwsNoEndl(ifstream& file)
131   {
132      int lines = skipws(file);           // remove all whitespaces
133
134      if (lines>0)
135         throw UnexpectedEndl;            // throw exception if an endl occured
136   }
137
138
139   //----------------------------------------------------------------------------------------------------------------------
140   // like skipwsNoEndl() but the function skipwsEx(), which addionally removes comments, is used
141   void skipwsExNoEndl(ifstream& file)
142   {
143      int lines = skipwsEx(file);         // remove all whitespaces and(!) comments
144
145      if (lines>0)
146         throw UnexpectedEndl;            // throw exception if an endl occured
147   }
148
149
150   //----------------------------------------------------------------------------------------------------------------------
151   // functions reads all whitespaces from an ifstream returning the number of encountered endl's
152   int skipws(ifstream& file)
153   {
154      file.clear();                 // reset stream state
155
156      int line =0;
157      int c;
158
159      // remove whitespaces counting endl's
160      do
161      {
162         c=file.get();
163         if(!isspace(c))
164            break;
165
166         if(c =='\n')               // if it's an endl: increment counter
167            line++;
168
169      } while (file.fail()==0);     // proceed while there are ws in the stream
170
171
172      // finish
173      file.clear();                 // reset stream state
174      file.putback((char) c);       // put the last character back in the stream cause it's no ws
175
176      return line;
177   }
178
179
180   //----------------------------------------------------------------------------------------------------------------------
181   // functions reads all whitespaces and comments from an ifstream returning the number of encountered endl's.
182   int skipwsEx(ifstream& file)
183   {
184      file.clear();                    // reset stream state
185
186      int line =0;
187      int c;
188
189      // while there are whitespaces or comments ...
190      do
191      {
192         c =file.get();                         // read char and ...
193         if(IsComment((char) c))                // ... if it's a comment ->  ...
194         {
195            file.ignore(INT_MAX, '\n');         // ... skip the rest of the line
196            line++;                             // increment line counter
197         }
198         else
199            if (c =='\n')                       // else if it's an endl
200               line++;                          // increment line counter
201            else
202               if(!isspace(c))                  // else if it's neither ws nor comment -> break
203                  break;
204
205      }
206      while (file.fail()==0);
207
208      // finish
209      file.clear();              // reset stream state
210      file.putback((char) c);    // put the last character back in the stream
211
212      return line;
213   }
214
215   //----------------------------------------------------------------------------------------------------------------------
216   // search stream for string 'szKey' followed by a '=' and read following string.
217   bool ReadKeyString(ifstream& file, const char*const& szKey, char* szString, const int& size, const int& maxLines)
218   {
219      szString[0]='\0';                               // 'delete' string
220      file.clear();
221      streampos curpos = file.tellg();                // preserve actual stream position
222      try{
223         SearchKey(file, szKey, maxLines, '[');       // search key
224      } catch(int errNo) { goto fail; }
225
226      if(file.get()!='=')                             // if next character is not '=' -> return false
227         goto fail;
228
229      if(skipws(file)!=0)                             // remove whitespaces
230         throw ValueMissing;
231
232      ReadString(file, szString, size);               // read string
233
234      file.seekg(curpos);                             // restore stream position
235      return true;
236
237      fail:
238      file.seekg(curpos);                             // restore stream position
239      return false;
240   }
241
242
243   //----------------------------------------------------------------------------------------------------------------------
244   // search stream for string 'szKey' followed by a '=', read following string and compare it with
245   // 'true'/'false'.
246   int ReadKeyBool(ifstream& file, const char*const& szKey, const int defaultValue, const int& maxLines)
247   {
248      char szString[STS];
249      if(!ReadKeyString(file, szKey, szString, STS, maxLines))    // read 'szKey = string'
250         return defaultValue;
251
252      if(strcmp(szString, "true")==0)           // if key was found: compare string to 'true' and 'false'
253         return true;
254      else
255         if(strcmp(szString, "false")==0)
256            return false;
257         else
258            throw BoolReadFailed;
259   }
260
261
262   //----------------------------------------------------------------------------------------------------------------------
263   // utility-function for template ReadExpNoEndl() - is called to examine a stream failstate.
264   void ReadFailed(ifstream& file)
265   {
266      file.clear();                    // reset stream state
267
268      int c;
269
270      if (EOF == (char) file.peek())   // peek and compare with EOF (end of file)
271         throw UnexpectedEOF;
272
273
274     // else: inspect if the character is a ws. if so it cannot be the one which caused the error. so go back
275     // in the stream until you find a none ws. this is the one the user wants to know;-)
276     while(isspace(c = file.get()) && file.fail()==0)
277         file.seekg(-2,ios::cur);
278
279      cIllChar = (char) c;
280
281      throw IllegalCharacter;    // throw exception
282   }
283
284
285   //----------------------------------------------------------------------------------------------------------------------
286   // function reads a string from an ifstream.
287   //   a) quoted string, i.e. string starts and ends with quotation marks
288   //   b) all whitespaces and the optional delimiter terminate the string
289   // The delimiter is left in the stream. The delimiter is returned.
290   // note: remind that a string can be empty.
291   char ReadString(ifstream& file, char* szString, const int& size, const char& cDelimiter)
292   {
293      file.clear();              // reset stream state
294
295      int i=0, c;
296
297      // 1. Check if first character is quotation mark
298      bool f_Quoted = false;
299      c = file.peek();
300      if(c=='"')                 // check for quotation mark
301      {
302         f_Quoted = true;        // set flag
303         file.get();             // remove the '"' from stream
304      }
305
306      // 2. Read string
307      do
308      {
309         c = file.get();                  // read character
310
311         if(file.fail()!=0)               // if the stream gets into a fail state
312         {
313            file.clear();                 // reset stream state
314            throw UnexpectedEOF;          // throw an exception. note: EOF is the only possible reason
315         }
316
317         // a) Quoted string: stop reading and terminate string if character is endl, EOF or quotation mark
318         if(f_Quoted)
319         {
320            if(c=='\n' || (char) c == EOF)    // endl or EOF (should not happen)
321            {
322               file.putback((char) c);
323               break;
324            }
325
326            if(c=='"')                       // quotation mark: do NOT put back in stream
327               break;
328         }
329         else
330            // b) 'unquoted' string: stop reading and terminate string if character is
331            //    whitespace, delimiter or EOF (should not happen)
332            if(isspace(c) || c==cDelimiter || (char) c == EOF )
333            {
334               file.putback((char) c);
335               break;
336            }
337
338         // copy character to string and continue
339         szString[i++] = (char) c;
340
341      } while(i<size-1);
342
343      szString[i]='\0';    // terminate string
344
345      return (char) c;     // return delimiter
346   }
347
348
349   //----------------------------------------------------------------------------------------------------------------------
350   // Sarch stream for string 'szKey'. The key must be at the begin of a line. If key is found, stream position is moved
351   // to first following non-whitespace. Else or in the case of any other error the previous position is restored.
352   //
353   // If exceptions are enabled they are thrown if:
354   //  a) key is not found
355   //  b) key is found but first following non-whitespace is EOF
356   //
357   // The return value is the number of encountered linefeeds.
358   int SearchKey(ifstream& file, const char*const& _szKey, const int& maxLines/*=INT_MAX*/, const char& delimiter/*=EOF*/,
359                  const bool& f_EnableExceptions/*=true*/)
360   {
361      file.clear();
362      streampos curPos = file.tellg();    // preserve actual stream position
363
364      int line = 0;
365      char szBuffer[STS+5];               // note: don't know anymore if this is necessary
366      szBuffer[0] = '\0';
367
368      strcpy(szKey, _szKey);              // store in global
369
370
371      // read linewise until match is found or EOF is encountered
372      while(strcmp(szBuffer, szKey)!=0 /* && file.fail()==0 */)
373      {
374         line += skipwsEx(file);                                              // skip all whitespaces and comments
375         if(file.peek()=='=')                                                 // skip '=' cause ReadString() won't do it
376            file.get();
377
378         // key wasn't found if EOF is reached
379         if(file.peek()==delimiter || line>maxLines || ReadString(file, szBuffer, STS, '=')==EOF)
380         {
381            file.clear();
382            file.seekg(curPos);     // restore stream position
383
384            if(f_EnableExceptions)  // throw exception or return '-1'
385               throw KeyNotFound;
386            else
387               return -1;
388         }
389      }
390
391      line += skipwsEx(file);       // position to next non-whitespace
392
393      if(file.fail()!=0)            // note: only way to get into a failstate is EOF
394      {
395         file.clear();
396         file.seekg(curPos);        // restore stream position
397
398         if(f_EnableExceptions)     // throw exception or return '-1'
399            throw UnexpectedEOF;
400         else
401            return -1;
402      }
403
404      return line;
405   }

Top