source: miniini/miniini/src/inisection.cpp @ 10:7107a8326645

Last change on this file since 10:7107a8326645 was 10:7107a8326645, checked in by laurent@…, 3 years ago

remove unused time.h

File size: 25.5 KB
Line 
1// Copyright (C) 2009-2010 Ferdinand Majerech
2// This file is part of MiniINI
3// For conditions of distribution and use, see copyright notice in LICENSE.txt
4
5#include <cstring>
6#ifndef _WIN32_WCE
7#include <cerrno>
8#endif
9#include <cstdlib>
10#include <cassert>
11#include <climits>
12
13#include "typedefs.h"               
14#include "globals.h"
15#include "log.h"
16#include "linetoken.h"
17#include "inisection.h"
18#include "util.h"
19
20#ifdef _WIN32_WCE
21float strtof(const char* nptr, char** endptr)
22{
23        float val = atof(nptr);
24        while(isspace(*nptr))
25                nptr++;
26        while(isdigit(*nptr))
27                nptr++;
28        if (*nptr == '.')
29                nptr++;
30        while(isdigit(*nptr))
31                nptr++;
32        *endptr = (char*) nptr;
33        return val;
34}
35
36#endif
37using namespace miniini_private;
38
39MINIINI_EXPORT ui INISection::temptagscap = 0;
40MINIINI_EXPORT c * * INISection::temptags = NULL;
41MINIINI_EXPORT ui INISection::tagcap = 0;
42MINIINI_EXPORT c * INISection::tagbuf = NULL;
43
44/// @cond PRIVATE
45///Perform given code 4 times. Used for manual loop unrolling.
46#define FOR_4(instr) \
47{\
48  instr;\
49  instr;\
50  instr;\
51  instr;\
52}
53/// @endcond
54
55        ///Allocates/initializes static data
56void INISection::InitTempData()
57{
58        assert(!temptags);
59        assert(!tagbuf);
60        temptagscap = 8;
61        temptags = new miniini_private::c * [temptagscap];
62        //MUST be over 6
63        tagcap = 64;
64        tagbuf = new miniini_private::c [tagcap];
65}
66   
67///Destroys static data
68void INISection::DestroyTempData()
69{
70    assert(temptags);
71    assert(tagbuf);
72    temptagscap = 0;
73    delete [] temptags;
74    temptags = NULL;
75    tagcap = 0;
76    delete [] tagbuf;
77    tagbuf = NULL;
78}
79
80
81inline LineToken INISection::TagName(const c * & currentcharref, ui & tagsize)
82{
83    //ptr to the current character
84    const c * currentchar = currentcharref;
85    //current character
86    register c ch;
87    for(;;)
88    {
89        //reallocate header buffer if not enough space to add new chars
90        //need 4 chars for unrolled part of the loop, 2 for trailing zeroes
91        //since tag and value are stored in the same buffer separated by a zero.
92        if(tagcap < tagsize + 6)
93        {
94            REALLOCATE(tagbuf, tagcap, tagsize, c);
95        }
96        //unrolled part of the loop (process 4 chars)
97        FOR_4
98        (
99        {
100            ch = *currentchar;
101            switch(ch)
102            {
103                case ' ':
104                case '\t':
105                {
106                    //ignore spaces
107                    break;
108                }
109                //CR, LF
110                case 10:
111                case 13:
112                {
113                    currentcharref = NextLine(currentchar);
114                    return LT_NAME;
115                    break;
116                }
117                //header start found.
118                case '[':
119                {
120                    currentcharref = currentchar;
121                    return LT_HEADER;
122                    break;
123                }
124                case '\0':
125                {
126                    currentcharref = currentchar;
127                    return LT_NAME;
128                    break;
129                }
130                default:
131                {       
132                    //value found, if tag name is not empty, start reading val
133                    if(ch == namevalsep)
134                    {
135                        if(tagsize)
136                        {
137                            //adding trailing zero to the tag name
138                            tagbuf[tagsize] = 0;
139                            ++tagsize;
140                            currentcharref = currentchar + 1;
141                            return LT_VAL;
142                        }
143                        else
144                        {
145                            WARNING("Empty tag name.");
146                            currentcharref = NextLine(currentchar);
147                            return LT_NAME;
148                        }
149                    } 
150                    if(ch == comment)
151                    {
152                        currentcharref = NextLine(currentchar);
153                        return LT_NAME;
154                    }
155                    //add new char to tag name.
156                    tagbuf[tagsize] = ch;
157                    ++tagsize;
158                    break;
159                }     
160            }
161            ++currentchar;
162        }
163        )
164    }
165}
166
167inline ui INISection::TagValue(const c * & currentcharref, ui tagsize)
168{
169    //ptr to the current character
170    const c * currentchar = currentcharref;
171    //current character
172    register c ch;
173    //reading characters of value
174    for(; ; ++currentchar)
175    {
176        ch = *currentchar;
177        switch(ch)
178        {
179            case ' ':
180            case '\t':
181            {
182                //ignore spaces
183                break;
184            }
185            //CR, LF
186            case 10:
187            case 13:
188            {
189                currentcharref = NextLine(currentchar);
190                return tagsize;
191                break;
192            }
193            case '\0':
194            {
195                currentcharref = currentchar;
196                return tagsize;
197                break;
198            }
199            default:
200            {
201                if(ch == comment)
202                {
203                    currentcharref = NextLine(currentchar);
204                    return tagsize;
205                }
206                //need 1 more char for trailing 0
207                if(tagcap < tagsize + 2)
208                {
209                    REALLOCATE(tagbuf, tagcap, tagsize, c);
210                }
211                //add new char to value
212                tagbuf[tagsize] = ch;
213                ++tagsize;
214            }
215        }
216    }
217}
218
219inline bool INISection::Header(const c * & currentcharref)
220{
221    //ptr to the current character
222    const c * currentchar = currentcharref;
223    //current character
224    register c ch;
225    //Name doesn't skip [ so we skip it here
226    ++currentchar;
227    //searching for first newline
228    for(; ; ++currentchar)
229    {
230        ch = *currentchar;
231        switch(ch)
232        {
233            //CR, LF
234            case 10:
235            case 13:
236            {
237                //not a header
238                currentcharref = NextLine(currentchar);
239                return false;
240                break;
241            }
242            case ']':
243            {
244                //if empty, ignore
245                return static_cast<bool>(currentchar - currentcharref);
246                break;
247            }
248            case '\0':
249            {
250                //not a header
251                currentcharref = currentchar;
252                return false;
253                break;
254            }
255            default:
256            {
257                if(ch == comment)
258                {
259                    currentcharref = NextLine(currentchar);
260                    return false;
261                }
262                break;
263            }
264        }
265    }
266}
267
268void INISection::Init(const c * const sectionname, const c * * const currentcharptr, 
269                      Allocator * const alloc)
270{
271    assert(sectionname);
272    assert(alloc);
273    assert(currentcharptr);
274    assert(*currentcharptr);
275    assert(temptags);
276    assert(tagbuf);
277    //Copying allocator ptr
278    Alloc = alloc;
279    //Copying name of the section
280    const ui namelen = strlen(sectionname) + 1;
281    assert(namelen > 1);
282    Name = Alloc->NewSpace(namelen);
283    memcpy(Name, sectionname, namelen);
284    //ptr to the current character in buffer
285    const c * currentchar = *currentcharptr;
286    //Iterating through lines in the buffer
287    while(*currentchar != '\0')
288    {
289        //size of tag name (if any) read
290        ui namesize = 0;
291        //goes to start of next line if nothing found,
292        //if value found, stops right after name=value separator,
293        //if header found, stays at beginning of line
294        LineToken token = TagName(currentchar, namesize);
295        //value found (this line is a name=value pair)
296        if(token == LT_VAL)
297        {
298            //size of name and value
299            ui tagsize = TagValue(currentchar, namesize);
300            //value is empty
301            if(namesize == tagsize)
302            {
303                WARNING("Empty value in a tag (no characters after name=value " 
304                        "separator. Ignoring. Section: %s", Name);
305                continue;
306            }
307            //adding trailing zero to the tag value
308            tagbuf[tagsize] = 0;
309            ++tagsize;
310            //if needed, reallocate the temp tags buffer
311            if(Length >= temptagscap)
312            {
313                REALLOCATE(temptags, temptagscap, Length, c *);
314            }
315            //Add new tag to temp tags buffer.
316            c * newtag = Alloc->NewSpace(tagsize);
317            memcpy(newtag, tagbuf, tagsize * sizeof(c));
318            Insert(temptags, Length, newtag);
319            ++Length;
320        }
321        //header found
322        else if(token == LT_HEADER)
323        {
324            //if this line is a header, we're finished loading the section
325            if(Header(currentchar))
326            {
327                break;
328            }
329            //else Header() leaves currentchar at the start of next line
330        }
331    }
332    //Updating line token ptr of the caller
333    *currentcharptr = currentchar;
334    //There are no tags in the section
335    if(!Length)
336    {
337        WARNING("Empty section. Section: %s", Name);
338    }
339    //Copy temp lines buffer to Lines
340    Tags = new c * [Length];
341    memcpy(Tags, temptags, Length * sizeof(c *));
342}
343
344INISection::~INISection()
345{
346    //There is no need to tell allocator to delete data here,
347    //since this is only called on INIFile destruction and that's
348    //when Allocator gets destroyed and deletes the data anyway.
349    //However, if/when section deletion is implemented, there
350    //will need to be a separate dtor to handle that case.
351    if(Tags)
352    {
353        delete [] Tags;
354    }
355}
356
357inline bool INISection::ReadString(const char * const name, const char * & out) const
358{
359    assert(name);
360    i pos = BinarySearch(Tags, Length, name);
361    if(pos >= 0)
362    {
363        out = Tags [pos] + strlen(Tags[pos]) + 1;
364        return true;
365    }
366    return false;
367}
368
369bool INISection::ReadInt(const char * const name, int & out) const
370{
371    assert(name);
372    //Points to char where strtol finished processing. Used to check for errors
373    c * tail;
374    //errno = 0;
375    const c * valstr;
376    //Requested tag does not exist
377    if(!ReadString(name, valstr))
378    {
379        return false;
380    }
381    const long tempout = strtol(valstr, &tail, 0);
382    const c tailc = tail[0];
383    if(tailc == valstr[0])
384    {
385        ERROR("Non-integer value in a tag where integer is expected."
386              "Section: %s Tag: %s Value: %s", Name, name, valstr);
387        return false;
388    }
389    /*if(errno)
390    {
391        //this is an error because in my tests strtol returned -1 on overflow.
392        ERROR("Integer value out of range."
393              "Section: %s Tag: %s Value: %s", Name, name, valstr);
394        return false;
395    }*/
396    #ifdef INI_DEBUG
397    if(tailc)
398    {
399        WARNING("Redunant characters in a tag where integer is "
400                "expected. Reading integer."
401                "Section: %s Tag: %s Value: %s", Name, name, valstr);
402    }
403    #endif
404    out = static_cast<int>(tempout);
405    return true;
406}
407
408bool INISection::ReadFloat(const char * const name, float & out) const
409{
410    assert(name);
411    //Points to char where strtof finished processing. Used to check for errors
412    c * tail;
413    //errno = 0;
414    const c * valstr;
415    //Requested tag does not exist
416    if(!ReadString(name, valstr))
417    {
418        return false;
419    }
420    const float tempout = strtof(valstr, &tail);
421    const c tailc = tail[0];
422    if(tailc == valstr[0])
423    {
424        ERROR("Non-float value in a tag where float is expected."
425              "Section: %s Tag: %s Value: %s", Name, name, valstr);
426        return false;
427    }
428    /*if(errno)
429    {
430        ERROR("Float value out of range."
431              "Section: %s Tag: %s Value: %s", Name, name, valstr);
432        return false;
433    }*/
434    #ifdef INI_DEBUG
435    if(tailc)
436    {
437        WARNING("Redunant characters in a tag where float is "
438                "expected. Reading float."
439                "Section: %s Tag: %s Value: %s", Name, name, valstr);
440    }
441    #endif
442    out = tempout;
443    return true;
444}
445
446bool INISection::ReadBool( const char * const name, bool & out) const
447{
448    assert(name);
449    const c * valstr;
450    //Requested tag does not exist
451    if(!ReadString(name, valstr))
452    {
453        return false;
454    }
455    //Parsing bool using the first character only
456    switch(valstr[0])
457    {
458        case 't':
459        case 'T':
460        case 'y':
461        case 'Y':
462        case '1':
463            out = true;
464            return true;
465        case 'f':
466        case 'F':
467        case 'n':
468        case 'N':
469        case '0':
470            out = false;
471            return true;
472        default:
473            return false;
474    }
475    return false;
476}
477
478unsigned INISection::ArraySize(const char * const name) const
479{
480    assert(name);
481    ui namelen = strlen(name);
482    //ptr to current tag
483    const c * tag;
484    //Points to char where strtol finished processing. Used to check for errors
485    c * tail;
486    //Index of current array element
487    ui elemidx; 
488    ui indicescap = 32;
489    ui indicessize = 0;
490    //Stores indices of found elements
491    ui * indices = new ui [indicescap];
492    for(ui idx = 0; idx < Length; ++idx)
493    {
494        tag = Tags[idx];
495        //if tag name starts by name
496        if(!strncmp(name, tag, namelen))
497        {
498            //errno = 0;
499            elemidx = strtol(tag + namelen, &tail, 0);   
500            //Note: If tag contains no integer, elemidx will be set to 0 which
501            //will never be read.
502            //Redunant chars after int - probably a different tag name or error
503            if(tail[0])
504            {
505                WARNING("Redunant characters after integer in tag name. "
506                        "Ignoring. Section: %s Tag name: %s", Name, tag);
507                continue;
508            }
509            #ifdef INI_DEBUG
510            //Huge (out of range) integer in tag name-
511            //Probably a different tag name or error
512            if(errno == ERANGE)
513            {
514                WARNING("Integer in tag name out of range. Ignoring. "
515                        "Section: %s Tag name: %s", Name, tag);
516                continue;
517            }
518            #endif
519            //elemidx can be 0 if tag contains no integer
520            if(elemidx)
521            {
522                //Reallocating indices if out of space
523                if(indicessize >= indicescap)
524                {
525                    REALLOCATE(indices, indicescap, indicessize, ui);
526                }
527                //Correct index, counting this array element
528                indices[indicessize] = elemidx - 1;
529                ++indicessize;
530            }
531        }
532    }
533    ui elems = 0;
534    bool finished = false;
535    //Count number of consecutive indices
536    while(!finished)
537    {
538        finished = true;
539        for(ui idx = 0; idx < indicessize; ++idx)
540        {
541            if(indices[idx] == elems)
542            {
543                ++elems;
544                finished = false;
545            }
546        }
547    }
548    return static_cast<unsigned>(elems);
549}
550
551unsigned INISection::ReadStrings(const char * const name, const char * * out, 
552                                 const unsigned cap) const
553{
554    assert(name);
555    assert(out);
556    ui namelen = strlen(name);
557    //we fill out with zeroes, so we can search for null ptr to determine how
558    //many elements we've read
559    memset(out, 0, sizeof(const char *) * cap);
560    //ptr to current tag
561    const c * tag;
562    //Points to char where strtol finished processing. Used to check for errors
563    c * tail;
564    //Index of current array element
565    ui elemidx; 
566    for(ui idx = 0; idx < Length; ++idx)
567    {
568        tag = Tags[idx];
569        //if tag name starts by name
570        if(!strncmp(name, tag, namelen))
571        {
572            //errno = 0;
573            elemidx = strtol(tag + namelen, &tail, 0);   
574            //Note: If tag contains no integer, elemidx will be set to 0 which
575            //will never be read.
576            //Redunant chars after int - probably a different tag name or error
577            if(tail[0])
578            {
579                WARNING("Redunant characters after integer in tag name. "
580                        "Ignoring. Section: %s Tag name: %s", Name, tag);
581                continue;
582            }
583            #ifdef INI_DEBUG
584            //Huge (out of range) integer in tag name-
585            //Probably a different tag name or error
586            if(errno == ERANGE)
587            {
588                WARNING("Integer in tag name out of range. Ignoring. "
589                        "Section: %s Tag name: %s", Name, tag);
590                continue;
591            }
592            #endif
593            //Out of capacity 
594            if(elemidx > cap)
595            {
596                WARNING("Array element out of range."
597                        "Section: %s Tag name: %s Range: 1-%d", Name, tag, cap);
598                continue;
599            }
600            //elemidx can be 0 if tag contains no integer
601            if(elemidx)
602            {
603                //Correct index at the end of name, reading array element
604                out[elemidx - 1] = (tag) + strlen(tag) + 1;
605            }
606        }
607    }
608    //return number of valid elements read
609    //(if, say, elem 19 is missing, only elems 0-18 are valid)
610    for(unsigned elem = 0; elem < cap; ++elem)
611    {
612        if(!out[elem])
613        {
614            return elem;
615        }
616    }
617    return cap;
618}
619
620unsigned INISection::ReadInts(const char * const name, int * out, 
621                              const unsigned cap) const
622{
623    assert(name);
624    assert(out);
625    //Array of value strings to be converted to ints
626    const c * * const valstrs = new const c * [cap];
627    //Number of strings read by ReadStrings to valstrs
628    const ui tempelems = ReadStrings(name, valstrs, cap);
629    //Ptr to current string in valstrs
630    const c * const * valstr = valstrs;
631    //When valstr reaches this, we've iterated over all the strings
632    const c * const * maxvalstr = valstrs + tempelems;
633    //Number of actual valid ints written to out
634    unsigned elems = 0;
635    //Points to char where strtol finished processing. Used to check for errors
636    c * tail;
637    //Iterating through strings read by ReadStrings and converting them to ints
638    for(; valstr < maxvalstr; ++valstr)
639    {   
640        //errno = 0;
641        const long tempelem = strtol(*valstr, &tail, 0);
642        const c tailc = tail[0];
643        if(tailc == *valstr[0])
644        {
645            ERROR("Non-integer value in an array tag where "
646                  "integer is expected. Terminating array "
647                  "reading. Section: %s Tag: %s%u Value: %s", 
648                  Name, name, elems + 1, *valstr);
649            break;
650        }
651        /*if(errno)
652        {
653            //this is an error because in some of my tests strtol returned -1 on overflow.
654            ERROR("Integer value in an array tag out of range."
655                  "Terminating array reading.Section: %s Tag: "
656                  "%s%u Value: %s", Name, name, elems + 1, *valstr);
657            break;
658        }*/
659        #ifdef INI_DEBUG
660        if(tailc)
661        {
662            WARNING("Redunant characters in a tag where integer "
663                    "is expected. Reading integer. Section: %s "
664                    "Tag: %s%u Value: %s", Name, name, elems + 1,
665                    *valstr);
666        }
667        #endif
668        out[elems] = static_cast<int>(tempelem);   
669        ++elems;
670    }
671    delete [] valstrs;
672    return elems;
673}       
674
675unsigned INISection::ReadFloats(const char * const name, float * out, 
676                                const unsigned cap) const
677{
678    assert(name);
679    assert(out);
680    //Array of value strings to be converted to floats
681    const c * * const valstrs = new const c * [cap];
682    //Number of strings read by ReadStrings to valstrs
683    const ui tempelems = ReadStrings(name, valstrs, cap);
684    //Ptr to current string in valstrs
685    const c * const * valstr = valstrs;
686    //When valstr reaches this, we've iterated over all the strings
687    const c * const * const maxvalstr = valstrs + tempelems;
688    //Number of actual valid floats written to out
689    unsigned elems = 0;
690    //Points to char where strtof finished processing. Used to check for errors
691    c * tail;
692    //Iterating through strings read by ReadStrings and converting them to floats
693    for(; valstr < maxvalstr; ++valstr)
694    {   
695        //errno = 0;
696        const f tempelem = strtof(*valstr, &tail);
697        const c tailc = tail[0];
698        if(tailc == *valstr[0])
699        {
700            ERROR("Non-float value in an array tag where "
701                  "float is expected. Terminating array "
702                  "reading. Section: %s Tag: %s%u Value: %s", 
703                  Name, name, elems + 1, *valstr);
704            break;
705        }
706        /*if(errno)
707        {
708            ERROR("Float value in an array tag out of range."
709                  "Terminating array reading.Section: %s Tag: "
710                  "%s%u Value: %s", Name, name, elems + 1, *valstr);
711            break;
712        }*/
713        #ifdef INI_DEBUG
714        if(tailc)
715        {
716            WARNING("Redunant characters in a tag where float "
717                    "is expected. Reading float. Section: %s "
718                    "Tag: %s%u Value: %s", Name, name, elems + 1,
719                    *valstr);
720        }
721        #endif
722        out[elems] = tempelem;   
723        ++elems;
724    }
725    delete [] valstrs;
726    return elems;
727}     
728
729unsigned INISection::ReadBools(const char * const name, bool * out, 
730                               const unsigned cap) const
731{
732    assert(name);
733    assert(out);
734    //Array of value strings to be converted to bools
735    const c * * const valstrs = new const c * [cap];
736    //Number of strings read by ReadStrings to valstrs
737    const ui tempelems = ReadStrings(name, valstrs, cap);
738    //Ptr to current string in valstrs
739    const c * const * valstr = valstrs;
740    //When valstr reaches this, we've iterated over all the strings
741    const c * const * const maxvalstr = valstrs + tempelems;
742    //Number of actual valid bools written to out
743    unsigned elems = 0;
744    //Iterating through strings read by ReadStrings and converting them to bools
745    for(; valstr < maxvalstr; ++valstr, ++elems)
746    {
747        switch(*valstr[0])
748        {
749            case 't':
750            case 'T':
751            case 'y':
752            case 'Y':
753            case '1':
754                out[elems] = true;
755                break;
756            case 'f':
757            case 'F':
758            case 'n':
759            case 'N':
760            case '0':
761                out[elems] = false;
762                break;
763            default:
764                goto BREAK_FOR;
765        }
766    }
767    BREAK_FOR:;
768    delete [] valstrs;
769    return elems;
770}
771
772#ifndef INI_NO_STL
773unsigned INISection::ReadStrings(const std::string & name, 
774                                 std::vector<std::string> & out) const
775{
776    const char * cname = name.c_str();
777    unsigned numelems = ArraySize(cname);
778    //Read data to this array first
779    const char * * tempstrs = new const char * [numelems];
780    numelems = ReadStrings(cname, tempstrs, numelems);
781    out.reserve(numelems);
782    //Move data to output vector
783    for(ui str = 0; str < numelems; ++str)
784    {
785        out.push_back(std::string(tempstrs[str]));
786    }
787    delete [] tempstrs;
788    return numelems;
789}
790
791unsigned INISection::ReadInts(const std::string & name, 
792                                 std::vector<int> & out) const
793{
794    const char * cname = name.c_str();
795    unsigned  numelems = ArraySize(cname);
796    //Read data to this array first
797    int * tempints = new int [numelems];
798    numelems = ReadInts(cname, tempints, numelems);
799    out.reserve(numelems);
800    //Move data to output vector
801    for(ui in = 0; in < numelems; ++in)
802    {
803        out.push_back(tempints[in]);
804    }
805    delete [] tempints;
806    return numelems;
807}
808
809unsigned INISection::ReadFloats(const std::string & name, 
810                                 std::vector<float> & out) const
811{
812    const char * cname = name.c_str();
813    unsigned  numelems = ArraySize(cname);
814    //Read data to this array first
815    float * tempfloats = new float [numelems];
816    numelems = ReadFloats(cname, tempfloats, numelems);
817    out.reserve(numelems);
818    //Move data to output vector
819    for(ui fl = 0; fl < numelems; ++fl)
820    {
821        out.push_back(tempfloats[fl]);
822    }
823    delete [] tempfloats;
824    return numelems;
825}
826
827unsigned INISection::ReadBools(const std::string & name, 
828                                 std::vector<bool> & out) const
829{
830    const char * cname = name.c_str();
831    unsigned numelems = ArraySize(cname);
832    //Read data to this array first
833    bool * tempbools = new bool [numelems];
834    numelems = ReadBools(cname, tempbools, numelems);
835    out.reserve(numelems);
836    //Move data to output vector
837    for(ui bo = 0; bo < numelems; ++bo)
838    {
839        out.push_back(tempbools[bo]);
840    }
841    delete [] tempbools;
842    return numelems;
843}
844#endif
845
846
847unsigned INISection::GetTags(const char *tags[], unsigned n) const {
848
849  const unsigned N = n > Length ? Length : n;
850
851  for(unsigned i = 0; i < N; i++)
852    tags[i] = Tags[i];
853
854  return static_cast<unsigned>(N);
855
856
857}
858
859unsigned INISection::GetTags(tagval tags[], unsigned n) const {
860
861  const unsigned N = n > Length ? Length : n;
862
863  for(unsigned  i = 0; i < N; i++) {
864    tags[i].tag = Tags[i];
865    tags[i].val = Tags[i] + strlen(Tags[i]) + 1;
866  }
867
868  return static_cast<unsigned>(N);
869
870
871}
872
873
874#ifndef INI_NO_STL
875void INISection::GetTags(std::vector<std::string>& tags) const {
876
877
878  for(unsigned i = 0; i < Length; i++)
879    tags.push_back(Tags[i]);
880
881
882}
883
884
885void INISection::GetTags(std::vector<std::pair<std::string, std::string> >& tags) const {
886
887
888  for(unsigned i = 0; i < Length; i++) {
889    std::pair<std::string, std::string> p(Tags[i], Tags[i]+strlen(Tags[i])+1);
890    tags.push_back(p);
891  }
892
893
894}
895
896
897
898#endif
Note: See TracBrowser for help on using the repository browser.