00001
00002
00003
00004
00005 #include <cstring>
00006 #include <cerrno>
00007 #include <cstdlib>
00008
00009 #include "typedefs.h"
00010 #include "globals.h"
00011 #include "log.h"
00012 #include "linetoken.h"
00013 #include "inisection.h"
00014
00015 using namespace miniini_private;
00016
00017 ui INISection::temptagscap = 0;
00018 c * * INISection::temptags = NULL;
00019 ui INISection::tagcap = 0;
00020 c * INISection::tagbuf = NULL;
00021
00022 INISection::INISection()
00023 :Name(NULL)
00024 ,Length(0)
00025 ,Tags(NULL)
00026 {}
00027
00028 bool INISection::Init(const c * const sectionname, c * * const outlinetoken)
00029 {
00030
00031 const ui namelen = strlen(sectionname) + 1;
00032 Name = new c[namelen];
00033 memcpy(Name, sectionname, namelen);
00034 c * linetoken = *outlinetoken;
00035
00036 linetoken = strtok( NULL, linesep );
00037
00038 ui tagsize;
00039
00040 c ch;
00041
00042 const c * chptr;
00043
00044 const c * chmax;
00045
00046 while( linetoken != NULL )
00047 {
00048 tagsize = 0;
00049
00050 const ui linesize = strlen(linetoken);
00051
00052 LineToken linepart = LT_NAME;
00053
00054 if(linesize >= tagcap)
00055 {
00056 tagcap = linesize + 1;
00057 delete [] tagbuf;
00058 tagbuf = new c[tagcap];
00059 }
00060
00061 for(chptr = linetoken, chmax = linetoken + linesize; chptr < chmax; ++chptr)
00062 {
00063 ch = *chptr;
00064
00065 if(ch == comment)
00066 {
00067
00068 if(linepart == LT_HEADER)
00069 {
00070 linepart = LT_NAME;
00071 WARNING("Part of a section header is commented out or there is a stray "
00072 "'[' character. Ignoring. Section: %s Line: %s", Name, linetoken);
00073 }
00074 break;
00075 }
00076
00077 if(linepart == LT_NAME)
00078 {
00079
00080 if(ch == ' ' || ch == '\t')
00081 {
00082 continue;
00083 }
00084
00085 if(ch == '=')
00086 {
00087
00088 if(!tagsize)
00089 {
00090 WARNING("Tag with an empty name (no characters before '='. Ignoring."
00091 "Section: %s Line: %s ", Name, linetoken);
00092 goto NEXT_LINE;
00093 }
00094
00095 tagbuf[tagsize] = 0;
00096 ++tagsize;
00097 linepart = LT_VAL;
00098 continue;
00099 }
00100
00101 if(ch == '[')
00102 {
00103 linepart = LT_HEADER;
00104 continue;
00105 }
00106
00107 tagbuf[tagsize] = ch;
00108 ++tagsize;
00109 }
00110
00111 else if(linepart == LT_VAL)
00112 {
00113
00114 if(ch == ' ' || ch == '\t')
00115 {
00116 continue;
00117 }
00118
00119 tagbuf[tagsize] = ch;
00120 ++tagsize;
00121 }
00122
00123 else if(linepart == LT_HEADER)
00124 {
00125
00126 if(ch == ' ' || ch == '\t')
00127 {
00128 continue;
00129 }
00130
00131
00132 if(ch == ']')
00133 {
00134 break;
00135 }
00136
00137 if(chptr == chmax - 1)
00138 {
00139 linepart = LT_NAME;
00140 WARNING("'[' character present in a line but there's no ']'."
00141 "This might be an incomplete header. Ignoring."
00142 "Section: %s Line: %s ", Name, linetoken);
00143
00144 }
00145 }
00146 }
00147
00148 if(linepart == LT_HEADER)
00149 {
00150 #ifdef INI_DEBUG
00151 if(tagsize)
00152 {
00153 WARNING("Unnecessary characters in a header line."
00154 "Section: %s Line: %s ", Name, linetoken);
00155 }
00156 #endif
00157 break;
00158 }
00159
00160 else if(linepart == LT_VAL)
00161 {
00162
00163 if(!tagbuf[tagsize - 1])
00164 {
00165 WARNING("Tag with an empty value (no characters after '='. Ignoring."
00166 "Section: %s Line: %s ", Name, linetoken);
00167 goto NEXT_LINE;
00168 }
00169
00170 tagbuf[tagsize] = 0;
00171 ++tagsize;
00172
00173 if(Length >= temptagscap)
00174 {
00175 temptagscap *= 2;
00176 c * * const temptemptags = new c * [temptagscap];
00177 memcpy(temptemptags, temptags, Length * sizeof(c *));
00178 delete [] temptags;
00179 temptags = temptemptags;
00180 }
00181
00182 temptags[Length] = new c [tagsize];
00183 memcpy(temptags[Length], tagbuf, tagsize * sizeof(c));
00184 ++Length;
00185 }
00186
00187 else if(tagsize > 1)
00188 {
00189 WARNING("Stray characters (nonempty line without tag, header or comments)"
00190 "Section: %s Line: %s ", Name, linetoken);
00191 }
00192 NEXT_LINE:
00193 linetoken = strtok( NULL, linesep );
00194 }
00195
00196 *outlinetoken = linetoken;
00197
00198 if(!Length)
00199 {
00200 WARNING("Empty section. Ignoring. Section: %s", Name);
00201 delete [] Name;
00202 Name = NULL;
00203 return false;
00204 }
00205
00206 Tags = new c * [Length];
00207 memcpy(Tags, temptags, Length * sizeof(c *));
00208 return true;
00209 }
00210
00211 INISection::~INISection()
00212 {
00213 if(Name)
00214 {
00215 delete [] Name;
00216 }
00217 if(Tags)
00218 {
00219 for(ui line = 0; line < Length; ++line)
00220 {
00221 delete [] Tags[line];
00222 }
00223 delete [] Tags;
00224 }
00225 }
00226
00227 const char * INISection::GetName() const
00228 {
00229 return Name;
00230 }
00231
00232 bool INISection::ReadString(const char * const name, const char * & out) const
00233 {
00234
00235 c * const * tagptr = Tags;
00236
00237 c * const * const maxtag = Tags + Length;
00238
00239 register const u8 * nameptr;
00240
00241 register u8 * tag;
00242
00243 register u8 namechar, tagchar;
00244
00245 for(; tagptr < maxtag; ++tagptr)
00246 {
00247
00248
00249 nameptr = reinterpret_cast<const u8 *>(name);
00250 tag = reinterpret_cast<u8 *>(*tagptr);
00251
00252 for(;;)
00253 {
00254 namechar = static_cast<u8>(*nameptr++);
00255 tagchar = static_cast<u8>(*(tag++));
00256
00257
00258 if (!namechar)
00259 {
00260 if(tagchar)
00261 {
00262 goto NEXT_TAG;
00263 }
00264
00265
00266 else
00267 {
00268 break;
00269 }
00270 }
00271
00272
00273 if(namechar != tagchar)
00274 {
00275 goto NEXT_TAG;
00276 }
00277 }
00278 out = reinterpret_cast<char *>(tag);
00279 return true;
00280 NEXT_TAG:;
00281 }
00282 return false;
00283
00284 }
00285
00286 bool INISection::ReadInt(const char * const name, int & out) const
00287 {
00288
00289 c * tail;
00290 errno = 0;
00291 const c * valstr;
00292
00293 if(!ReadString(name, valstr))
00294 {
00295 return false;
00296 }
00297 const long tempout = strtol(valstr, &tail, 0);
00298 const c tailc = tail[0];
00299 if(tailc == valstr[0])
00300 {
00301 ERROR("Non-integer value in a tag where integer is expected."
00302 "Section: %s Tag: %s Value: %s", Name, name, valstr);
00303 return false;
00304 }
00305 if(errno)
00306 {
00307
00308 ERROR("Integer value out of range."
00309 "Section: %s Tag: %s Value: %s", Name, name, valstr);
00310 return false;
00311 }
00312 #ifdef INI_DEBUG
00313 if(tailc)
00314 {
00315 WARNING("Redunant characters in a tag where integer is "
00316 "expected. Reading integer."
00317 "Section: %s Tag: %s Value: %s", Name, name, valstr);
00318 }
00319 #endif
00320 out = static_cast<int>(tempout);
00321 return true;
00322 }
00323
00324 bool INISection::ReadFloat(const char * const name, float & out) const
00325 {
00326
00327 c * tail;
00328 errno = 0;
00329 const c * valstr;
00330
00331 if(!ReadString(name, valstr))
00332 {
00333 return false;
00334 }
00335 const float tempout = strtof(valstr, &tail);
00336 const c tailc = tail[0];
00337 if(tailc == valstr[0])
00338 {
00339 ERROR("Non-float value in a tag where float is expected."
00340 "Section: %s Tag: %s Value: %s", Name, name, valstr);
00341 return false;
00342 }
00343 if(errno)
00344 {
00345 ERROR("Float value out of range."
00346 "Section: %s Tag: %s Value: %s", Name, name, valstr);
00347 return false;
00348 }
00349 #ifdef INI_DEBUG
00350 if(tailc)
00351 {
00352 WARNING("Redunant characters in a tag where float is "
00353 "expected. Reading float."
00354 "Section: %s Tag: %s Value: %s", Name, name, valstr);
00355 }
00356 #endif
00357 out = tempout;
00358 return true;
00359 }
00360
00361 bool INISection::ReadBool( const char * const name, bool & out) const
00362 {
00363 const c * valstr;
00364
00365 if(!ReadString(name, valstr))
00366 {
00367 return false;
00368 }
00369
00370 for(ui b = 0; b < BOOLNUM; ++b)
00371 {
00372 if(!strcmp(valstr, truebools[b]))
00373 {
00374 out = true;
00375 return true;
00376 }
00377 if(!strcmp(valstr, falsebools[b]))
00378 {
00379 out = false;
00380 return true;
00381 }
00382 }
00383 return false;
00384 }
00385
00386 unsigned INISection::ReadStrings(const char * const name, const char * * out,
00387 const unsigned cap) const
00388 {
00389
00390 const c * const * tagptr = Tags;
00391
00392 const c * tag;
00393
00394 const c * const * const maxtagptr = Tags + Length;
00395
00396 ui elems = 0;
00397 const ui namelen = strlen(name);
00398
00399 ui elemidx;
00400
00401
00402 c * tail;
00403 for(; tagptr < maxtagptr; ++tagptr)
00404 {
00405 tag = *tagptr;
00406
00407 if(!strncmp(name, tag, namelen))
00408 {
00409
00410 elemidx = strtol(tag + namelen, &tail, 0);
00411 const c tailc = tail[0];
00412
00413
00414 if(tailc == tag[namelen])
00415 {
00416 continue;
00417 }
00418
00419
00420 if(errno)
00421 {
00422 WARNING("Integer in tag name out of range. Ignoring. "
00423 "Section: %s Tag name: %s", Name, tag);
00424 continue;
00425 }
00426
00427 if(tailc)
00428 {
00429 WARNING("Redunant characters after integer in tag name. "
00430 "Ignoring. Section: %s Tag name: %s", Name, tag);
00431 continue;
00432 }
00433
00434 if(elemidx > cap || elemidx < 1)
00435 {
00436 WARNING("Integer in tag name out of range. Ignoring. Section: "
00437 "%s Tag name: %s Range: 1-%d", Name, tag, cap);
00438 continue;
00439 }
00440
00441 if(elemidx == elems + 1)
00442 {
00443 ++elems;
00444 out[elemidx - 1] = (tag) + strlen(tag) + 1;
00445 }
00446
00447 else
00448 {
00449 WARNING("Array tag name in incorrect order (e.g. tag3= "
00450 "after tag1= without tag2= in between) Ignoring "
00451 "the tag. Section: %s Tag name: %s ", Name, tag);
00452 }
00453 }
00454 }
00455 return static_cast<unsigned>(elems);
00456 }
00457
00458 unsigned INISection::ReadInts(const char * const name, int * out,
00459 const unsigned cap) const
00460 {
00461
00462 const c * * const valstrs = new const c * [cap];
00463
00464 const ui tempelems = ReadStrings(name, valstrs, cap);
00465
00466 const c * const * valstr = valstrs;
00467
00468 const c * const * maxvalstr = valstrs + tempelems;
00469
00470 unsigned elems = 0;
00471
00472 c * tail;
00473
00474 for(; valstr < maxvalstr; ++valstr)
00475 {
00476 errno = 0;
00477 const long tempelem = strtol(*valstr, &tail, 0);
00478 const c tailc = tail[0];
00479 if(tailc == *valstr[0])
00480 {
00481 ERROR("Non-integer value in an array tag where "
00482 "integer is expected. Terminating array "
00483 "reading. Section: %s Tag: %s%u Value: %s",
00484 Name, name, elems + 1, *valstr);
00485 break;
00486 }
00487 if(errno)
00488 {
00489
00490 ERROR("Integer value in an array tag out of range."
00491 "Terminating array reading.Section: %s Tag: "
00492 "%s%u Value: %s", Name, name, elems + 1, *valstr);
00493 break;
00494 }
00495 #ifdef INI_DEBUG
00496 if(tailc)
00497 {
00498 WARNING("Redunant characters in a tag where integer "
00499 "is expected. Reading integer. Section: %s "
00500 "Tag: %s%u Value: %s", Name, name, elems + 1,
00501 *valstr);
00502 }
00503 #endif
00504 out[elems] = static_cast<int>(tempelem);
00505 ++elems;
00506 }
00507 delete [] valstrs;
00508 return elems;
00509 }
00510
00511 unsigned INISection::ReadFloats(const char * const name, float * out,
00512 const unsigned cap) const
00513 {
00514
00515 const c * * const valstrs = new const c * [cap];
00516
00517 const ui tempelems = ReadStrings(name, valstrs, cap);
00518
00519 const c * const * valstr = valstrs;
00520
00521 const c * const * const maxvalstr = valstrs + tempelems;
00522
00523 unsigned elems = 0;
00524
00525 c * tail;
00526
00527 for(; valstr < maxvalstr; ++valstr)
00528 {
00529 errno = 0;
00530 const f tempelem = strtof(*valstr, &tail);
00531 const c tailc = tail[0];
00532 if(tailc == *valstr[0])
00533 {
00534 ERROR("Non-float value in an array tag where "
00535 "float is expected. Terminating array "
00536 "reading. Section: %s Tag: %s%u Value: %s",
00537 Name, name, elems + 1, *valstr);
00538 break;
00539 }
00540 if(errno)
00541 {
00542 ERROR("Float value in an array tag out of range."
00543 "Terminating array reading.Section: %s Tag: "
00544 "%s%u Value: %s", Name, name, elems + 1, *valstr);
00545 break;
00546 }
00547 #ifdef INI_DEBUG
00548 if(tailc)
00549 {
00550 WARNING("Redunant characters in a tag where float "
00551 "is expected. Reading float. Section: %s "
00552 "Tag: %s%u Value: %s", Name, name, elems + 1,
00553 *valstr);
00554 }
00555 #endif
00556 out[elems] = tempelem;
00557 ++elems;
00558 }
00559 delete [] valstrs;
00560 return elems;
00561 }
00562
00563 unsigned INISection::ReadBools(const char * const name, bool * out,
00564 const unsigned cap) const
00565 {
00566
00567 const c * * const valstrs = new const c * [cap];
00568
00569 const ui tempelems = ReadStrings(name, valstrs, cap);
00570
00571 const c * const * valstr = valstrs;
00572
00573 const c * const * const maxvalstr = valstrs + tempelems;
00574
00575 unsigned elems = 0;
00576
00577 for(; valstr < maxvalstr; ++valstr, ++elems)
00578 {
00579
00580 for(ui b = 0; b < BOOLNUM; ++b)
00581 {
00582 if(!strcmp(*valstr, truebools[b]))
00583 {
00584 out[elems] = true;
00585 goto NEXT_ELEM;
00586 }
00587 if(!strcmp(*valstr, falsebools[b]))
00588 {
00589 out[elems] = false;
00590 goto NEXT_ELEM;
00591 }
00592 }
00593
00594 WARNING("Non-bool value in an array tag where bool is expected. "
00595 "Terminating array reading. Section: %s Tag: %s%u "
00596 "Value: %s", Name, name, elems + 1, *valstr );
00597 break;
00598 NEXT_ELEM:;
00599 }
00600 delete [] valstrs;
00601 return elems;
00602 }
00603
00604 unsigned INISection::GetLength() const
00605 {
00606 return static_cast<unsigned>(Length);
00607 }
00608