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 |