LCOV - code coverage report
Current view: top level - source/app - ini.cpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 87.5 % 72 63
Test Date: 2026-08-30 22:23:37 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /** \file ini.cpp
       2              :  * \author Jared R. Males
       3              :  * \brief Implementation of the The inih  ini-style, file parser modified for mxlib.
       4              :  *
       5              :  * \ingroup mxApp_files
       6              :  *
       7              :  */
       8              : 
       9              : #ifndef INI_COMMENT_CHAR
      10              : #define INI_COMMENT_CHAR ( '#' )
      11              : #endif
      12              : 
      13              : /* Nonzero to allow multi-line value parsing, in the style of Python's
      14              :    ConfigParser. If allowed, ini_parse() will call the handler with the same
      15              :    name for each subsequent line parsed. */
      16              : #ifndef INI_ALLOW_MULTILINE
      17              : #define INI_ALLOW_MULTILINE 1
      18              : #endif
      19              : 
      20              : /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
      21              :    the file. See http://code.google.com/p/inih/issues/detail?id=21 */
      22              : #ifndef INI_ALLOW_BOM
      23              : #define INI_ALLOW_BOM 1
      24              : #endif
      25              : 
      26              : /* Nonzero to use stack, zero to use heap (malloc/free). */
      27              : #ifndef INI_USE_STACK
      28              : #define INI_USE_STACK 0
      29              : #endif
      30              : 
      31              : /* Stop parsing on first error (default is to stop parsing). */
      32              : #ifndef INI_STOP_ON_FIRST_ERROR
      33              : #define INI_STOP_ON_FIRST_ERROR 0
      34              : #endif
      35              : 
      36              : /* Maximum line length for any line in INI file. */
      37              : #ifndef INI_MAX_LINE
      38              : #define INI_MAX_LINE 1024
      39              : #endif
      40              : 
      41              : #define MAX_SECTION 50
      42              : #define MAX_NAME 50
      43              : 
      44              : #if !INI_USE_STACK
      45              : #include <stdlib.h>
      46              : #endif
      47              : 
      48              : #include "app/ini.hpp"
      49              : 
      50              : /* Strip whitespace chars off end of given string, in place. Return s. */
      51          468 : static char *rstrip( char *s )
      52              : {
      53          468 :     char *p = s + strlen( s );
      54          686 :     while( p > s && isspace( (unsigned char)( *--p ) ) )
      55          218 :         *p = '\0';
      56          468 :     return s;
      57              : }
      58              : 
      59              : /* Return pointer to first non-whitespace char in given string. */
      60          340 : static char *lskip( const char *s )
      61              : {
      62          348 :     while( *s && isspace( (unsigned char)( *s ) ) )
      63            8 :         s++;
      64          340 :     return (char *)s;
      65              : }
      66              : 
      67              : /* Return pointer to first char c or ';' comment in given string, or pointer to
      68              :    null at end of string if neither found. ';' must be prefixed by a whitespace
      69              :    character to register as a comment. */
      70          300 : static char *find_char_or_comment( const char *s, char c )
      71              : {
      72          300 :     int was_whitespace = 0;
      73         1928 :     while( *s && *s != c && !( was_whitespace && *s == INI_COMMENT_CHAR ) )
      74              :     {
      75         1628 :         was_whitespace = isspace( (unsigned char)( *s ) );
      76         1628 :         s++;
      77              :     }
      78          300 :     return (char *)s;
      79              : }
      80              : 
      81              : /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
      82          171 : static char *strncpy0( char *dest, const char *src, size_t size )
      83              : {
      84          171 :     strncpy( dest, src, size );
      85          171 :     dest[size - 1] = '\0';
      86          171 :     return dest;
      87              : }
      88              : 
      89           34 : int ini_parse_file( FILE *file, int ( *handler )( void *, const char *, const char *, const char * ), void *user )
      90              : {
      91              :     /* Uses a fair bit of stack (use heap instead if you need to) */
      92              : #if INI_USE_STACK
      93              :     char line[INI_MAX_LINE];
      94              : #else
      95              :     char *line;
      96              : #endif
      97           34 :     char section[MAX_SECTION] = "";
      98           34 :     char prev_name[MAX_NAME] = "";
      99              : 
     100              :     char *start;
     101              :     char *end;
     102              :     char *name;
     103              :     char *value;
     104           34 :     int lineno = 0;
     105           34 :     int error = 0;
     106              : 
     107              : #if !INI_USE_STACK
     108           34 :     line = (char *)malloc( INI_MAX_LINE );
     109           34 :     if( !line )
     110              :     {
     111            0 :         return -2;
     112              :     }
     113              : #endif
     114              : 
     115              :     /* Scan through file line by line */
     116          246 :     while( fgets( line, INI_MAX_LINE, file ) != NULL )
     117              :     {
     118          212 :         lineno++;
     119              : 
     120          212 :         start = line;
     121              : #if INI_ALLOW_BOM
     122          212 :         if( lineno == 1 && (unsigned char)start[0] == 0xEF && (unsigned char)start[1] == 0xBB &&
     123            0 :             (unsigned char)start[2] == 0xBF )
     124              :         {
     125            0 :             start += 3;
     126              :         }
     127              : #endif
     128          212 :         start = lskip( rstrip( start ) );
     129              : 
     130          212 :         if( *start == INI_COMMENT_CHAR || *start == '#' )
     131              :         {
     132              :             /* Per Python ConfigParser, allow '#' comments at start of line */
     133              :         }
     134              : #if INI_ALLOW_MULTILINE
     135          212 :         else if( *prev_name && *start && start > line )
     136              :         {
     137              :             /* Non-black line with leading whitespace, treat as continuation
     138              :                of previous name's value (as per Python ConfigParser). */
     139            2 :             if( !handler( user, section, prev_name, start ) && !error )
     140            0 :                 error = lineno;
     141              :         }
     142              : #endif
     143          210 :         else if( *start == '[' )
     144              :         {
     145              :             /* A "[section]" line */
     146           44 :             end = find_char_or_comment( start + 1, ']' );
     147           44 :             if( *end == ']' )
     148              :             {
     149           43 :                 *end = '\0';
     150           43 :                 strncpy0( section, start + 1, sizeof( section ) );
     151           43 :                 *prev_name = '\0';
     152              :             }
     153            1 :             else if( !error )
     154              :             {
     155              :                 /* No ']' found on section line */
     156            1 :                 error = lineno;
     157              :             }
     158              :         }
     159          166 :         else if( *start && *start != INI_COMMENT_CHAR )
     160              :         {
     161              :             /* Not a comment, must be a name[=:]value pair */
     162          128 :             end = find_char_or_comment( start, '=' );
     163          128 :             if( *end != '=' )
     164              :             {
     165            0 :                 end = find_char_or_comment( start, ':' );
     166              :             }
     167          128 :             if( *end == '=' || *end == ':' )
     168              :             {
     169          128 :                 *end = '\0';
     170          128 :                 name = rstrip( start );
     171          128 :                 value = lskip( end + 1 );
     172          128 :                 end = find_char_or_comment( value, '\0' );
     173          128 :                 if( *end == INI_COMMENT_CHAR )
     174            0 :                     *end = '\0';
     175          128 :                 rstrip( value );
     176              : 
     177              :                 /* Valid name[=:]value pair found, call handler */
     178          128 :                 strncpy0( prev_name, name, sizeof( prev_name ) );
     179          128 :                 if( !handler( user, section, name, value ) && !error )
     180            0 :                     error = lineno;
     181              :             }
     182            0 :             else if( !error )
     183              :             {
     184              :                 /* No '=' or ':' found on name[=:]value line */
     185            0 :                 error = lineno;
     186              :             }
     187              :         }
     188              : 
     189              : #if INI_STOP_ON_FIRST_ERROR
     190              :         if( error )
     191              :             break;
     192              : #endif
     193              :     }
     194              : 
     195              : #if !INI_USE_STACK
     196           34 :     free( line );
     197              : #endif
     198              : 
     199           34 :     return error;
     200              : }
     201              : 
     202           37 : int ini_parse( const char *filename, int ( *handler )( void *, const char *, const char *, const char * ), void *user )
     203              : {
     204              :     FILE *file;
     205              :     int error;
     206              : 
     207           37 :     file = fopen( filename, "r" );
     208           37 :     if( !file )
     209            3 :         return -1;
     210           34 :     error = ini_parse_file( file, handler, user );
     211           34 :     fclose( file );
     212           34 :     return error;
     213              : }
        

Generated by: LCOV version 2.0-1