EnglishРусский  

   ..

   defines.c

   defines.h

   ini.c

   ini.h

The project is closed! You can look at a new scripting language. It is available on GitHub.
Also, try our open source cross-platform automation software.

Ads

Installer and installation software
Commercial and Freeware installers.

  1 /*
  2 ** This file is placed into the public domain on February 22, 1996,  by
  3 ** its author: Carey Bloodworth
  4 **
  5 ** Modifications:
  6 **
  7 ** 07-Dec-1999 by
  8 ** Bob Stout & Jon Guthrie
  9 **  General cleanup, use NUL (in SNIPTYPE.H) instead of NULL where appropriate.
 10 **  Allow spaces in tag names.
 11 **  Allow strings in quotes.
 12 **  Allow trailing comments.
 13 **  Allow embedded comment separator(s) in quoted strings.
 14 **  Add comments.
 15 **  ReadCfg() now returns the number of variables found if no error occurred.
 16 **  Changed integer type to short.
 17 **  Use cant() calls in lieu of fopen() calls,
 18 **    include ERRORS.H for prototype.
 19 **  Fix parsing error with null string data.
 20 */
 21 
 22 #include <stdio.h>
 23 #include <string.h>
 24 #include <ctype.h>
 25 #include <stdlib.h>
 26 
 27 #include "ini.h"
 28 
 29 #define BUFFERSIZE (INI_LINESIZE + 2)
 30 
 31 enum LineTypes {
 32       EmptyLine = 0, CommentLine = 1, InSection = 2, NotInSection = 3,
 33       NewSection = 4, FoundSection = 5, LeavingSection = 6
 34 };
 35 
 36 
 37 /*
 38 **  ferrorf()
 39 **
 40 **  Prints error message with printf() formatting syntax, then a colon,
 41 **  then a message corresponding to the value of errno, then a newline.
 42 **  Output is to filehandle.
 43 **
 44 **  Public Domain by Mark R. Devlin, free usage is permitted.
 45 */
 46  
 47 #include <stdlib.h>
 48 #include <stdarg.h>
 49 #include <string.h>
 50 #include <errno.h>
 51  
 52 int ferrorf(FILE *filehandle, const char *format, ...)
 53 {
 54       int vfp, fp;
 55       va_list vargs;
 56  
 57       vfp = fp = 0;
 58       va_start(vargs, format);
 59       vfp = vfprintf(filehandle, format, vargs);
 60       va_end(vargs);
 61       fp = fprintf(filehandle, ": %s\n", sys_errlist[errno]);
 62       return ((vfp==EOF || fp==EOF) ? EOF : (vfp+fp));
 63 }
 64 
 65 /*
 66 **  chgext.c - Change a file's extension
 67 **
 68 **  public domain by Bob Stout
 69 **
 70 **  Arguments: 1 - Pathname
 71 **             2 - Old extension (NULL if don't care)
 72 **             3 - New extension
 73 **
 74 **  Returns: Pathname or NULL if failed
 75 **
 76 **  Note: Pathname buffer must be long enough to append new extension
 77 **
 78 **  Side effect: Converts Unix style pathnames to DOS style
 79 */
 80 
 81 char *chgext(char *path, char *oldext, char *newext)
 82 {
 83       char *p;
 84 
 85       /* Convert to DOS-style path name */
 86 
 87       for (p = path; *p; ++p)
 88             if ('/' == *p)
 89                   *p = '\\';
 90 
 91       /* Find extension or point to end for appending */
 92 
 93       if (NULL == (p = strrchr(path, '.')) || NULL != strchr(p, '\\'))
 94             p = strcpy(&path[strlen(path)], ".");
 95       ++p;
 96 
 97       /* Check for old extension */
 98 
 99       if (oldext && strcmp(p, oldext))
100             return NULL;
101 
102       /* Add new extension */
103 
104       while ('.' == *newext)
105             ++newext;
106       strncpy(p, newext, 3);
107 
108       /*
109       ** Added to insure string is properly terminated. Without this, if
110       ** the new extension is longer than the old, we lose the terminator.
111       */
112 
113       p[strlen(newext)] = '\0';
114 
115       return path;
116 }
117 
118 
119 
120 /*
121 **  cant() - An fopen() replacement with error trapping
122 **
123 **  Call just as you would fopen(), but make sure your exit functions are
124 **  registered with atexit().
125 **
126 **  public domain by Bob Stout
127 */
128 
129 FILE *cant(char *fname, char *fmode)
130 {
131       FILE *fp;
132 
133       if (NULL == (fp = fopen(fname, fmode)))
134       {
135             ferrorf(stderr, "Can't open %s", fname);
136             exit(EXIT_FAILURE);
137       }
138       return fp;
139 }
140 
141 
142 
143 /*
144 **  StripLeadingSpaces() - Strips leading spaces from a string.
145 **
146 **  Paramters: 1 - String.
147 **
148 **  Returns: Pointer to first non-whitespace character in the string.
149 **
150 **  Note: This does not modify the original string.
151 */
152 
153 static char *StripLeadingSpaces(char *string)
154 {
155       if (!string || (0 == strlen(string)))
156             return NULL;
157 
158       while ((*string) && (isspace(*string)))
159             string++;
160       return string;
161 }
162 
163 /*
164 **  StripTrailingSpaces() - Strips traling spaces from a string.
165 **
166 **  Paramters: 1 - String.
167 **
168 **  Returns: Pointer to the string.
169 **
170 **  Note: This does modify the original string.
171 */
172 
173 static char *StripTrailingSpaces(char *string)
174 {
175       if (!string || (0 == strlen(string)))
176             return NULL;
177 
178       while (isspace(LAST_CHAR(string)))
179             LAST_CHAR(string) = NUL;
180 
181       return string;
182 }
183 
184 /*
185 **  ReadLine() - Read a line from the active file
186 **
187 **  Paramters: 1 - File pointer of the active file.
188 **             2 - Pointer to the line's storage.
189 **
190 **  Returns: Length of line or EOF.
191 **
192 **  Side effects: Strips newline characters (DOS, Unix, or Mac).
193 */
194 
195 static int ReadLine(FILE *fp, char *line)
196 {
197       char *cp;
198 
199       cp = fgets(line, INI_LINESIZE, fp);
200 
201       if (NULL == cp)
202       {
203             *line = 0;
204             return EOF;
205       }
206       if (feof(fp))
207       {
208             *line = 0;
209             return EOF;
210       }
211       if (0 != ferror(fp))
212       {
213             *line = 0;
214             return EOF;
215       }
216 
217       /*
218       **  Allow both DOS and Unix style newlines.
219       */
220       
221       while (strlen(line) && strchr("\n\r", LAST_CHAR(line)))
222             LAST_CHAR(line) = NUL;
223 
224       return strlen(line);
225 }
226 
227 /*
228 **  StrEq() - Case-insensitive string compare.
229 **
230 **  Paramters: 1 - First string.
231 **             2 - second string.
232 **
233 **  Returns: True_ if strings match, else False_.
234 */
235 
236 static Boolean_T StrEq(char *s1, char *s2)
237 {
238       while (tolower(*s1) == tolower(*s2))
239       {
240             if (NUL == *s1)
241                   return True_;
242             s1++;
243             s2++;
244       }
245       return False_;
246 }
247 
248 /*
249 **  ParseLine() - This routine divides the line into two parts. The
250 **                variable, and the 'string'.
251 **
252 **  Paramters: 1 - The line to parse:
253 **             2 - Pointer to variable name storage.
254 **             3 = Pointer to textual data storage.
255 **
256 **  Returns: Nothing.
257 */
258 
259 static void ParseLine(char *line, char *var, char *data)
260 {
261       int len = 0;
262 
263       line = StripLeadingSpaces(line);
264       strcpy(var, line);
265       strcpy(data, "");
266 
267       while (*line)
268       {
269             char *ptr;
270 
271             if (/*isspace(*line) || */ ('=' == *line))
272             {
273                   var[len] = 0;
274                   var  = StripTrailingSpaces(var);
275                   line = StripLeadingSpaces(line+1);
276 
277                   /* Remove a possible '=' */
278                   /* This could allow var==string in some cases. No big deal*/
279 
280                   while (line && '=' == *line)
281                         line = StripLeadingSpaces(line+1);
282                   if (line)
283                   {
284                         strcpy(data, line);
285                         if (NULL != (ptr = strrchr(data, ';')) &&
286                             NULL == strchr(ptr, '\"'))
287                         {
288                               *ptr = NUL;
289                         }
290                         StripTrailingSpaces(data);
291                   }
292                   return;
293             }
294             else
295             {
296                   line++;
297                   len++;
298             }
299       }
300 }
301 
302 /*
303 **  SectionLine() - This routine checks each line of the file and identifies
304 **                  only those lines that are within the right section.
305 **
306 **  Paramters: 1 - The line to scan.
307 **             2 - The specific section wanted.
308 **             3 - The current section name.
309 **
310 **  Returns: Line type.
311 */
312 
313 static enum LineTypes SectionLine(char *line,
314                                   char *SectionWanted,
315                                   char *CurrentSection)
316 {
317       enum LineTypes linetype;
318 
319       line = StripLeadingSpaces(line);
320       if (!line || NUL == *line)
321             return EmptyLine;
322 
323       /*
324       ** Comments are started with a "%", ";" or "#"
325       */
326 
327       if (';' == *line)
328             return CommentLine;
329       if ('%' == *line)
330             return CommentLine;
331       if ('#' == *line)
332             return CommentLine;
333 
334       if ('[' == line[0])  /* Section Header */
335       {
336             linetype = NewSection;
337             if (StrEq(CurrentSection, SectionWanted))
338                   linetype = LeavingSection;
339 
340             strcpy(CurrentSection, line);
341             if (StrEq(line, SectionWanted))
342                   linetype = FoundSection;
343       }
344       else
345       {            /* Just a regular line */
346             linetype = NotInSection;
347             if (StrEq(CurrentSection, SectionWanted))
348                   linetype = InSection;
349       }
350 
351       return linetype;
352 }
353 
354 /*
355 **  ReadCfg() - Reads a .ini / .cfg file. May read multiple lines within the
356 **              specified section.
357 **
358 **  Paramters: 1 - File name
359 **             2 - Section name
360 **             3 - Array of CfgStruct pointers
361 **
362 **  Returns: Number of variables located
363 **           -1 if any type spec failed
364 **           -2 for any type of file error
365 */
366 
367 int ReadCfg(const char *FileName, char *SectionName, struct CfgStruct *MyVars)
368 {
369       FILE *CfgFile;
370       char line[BUFFERSIZE];
371       char SectionWanted[BUFFERSIZE];
372       char CurrentSection[BUFFERSIZE];
373       enum LineTypes linetype;
374       char var[BUFFERSIZE];
375       char data[BUFFERSIZE], *dp;
376       int retval = 0;
377       struct CfgStruct *mv;
378 
379       CfgFile = cant((char *)FileName, "r");
380 
381       strcpy(CurrentSection, "[]");
382       sprintf(SectionWanted, "[%s]", SectionName);
383 
384       while (EOF != ReadLine(CfgFile, line))
385       {
386             linetype = SectionLine(line, SectionWanted, CurrentSection);
387             switch (linetype)
388             {
389             case EmptyLine:
390                   break;  /* Nothing to parse */
391 
392             case CommentLine:
393                   break;  /* Nothing to parse */
394 
395             case InSection:              /* In our section, parse it. */
396             {
397                   ParseLine(line, var, data);
398 
399                   for (mv = MyVars; mv->Name; ++mv)
400                   {
401                         if (StrEq(mv->Name, var))
402                         {
403                               switch (mv->VarType)
404                               {
405                               case Cfg_String:
406                                     if ('\"' == *data)
407                                     {
408                                           dp = data + 1;
409                                           data[strlen(data)-1] = NUL;
410                                     }
411                                     else  dp = data;
412                                     /*
413                                     ** Use sprintf to assure embedded
414                                     ** escape sequences are handled.
415                                     */
416                                     sprintf(mv->DataPtr, dp);
417                                     ++retval;
418                                     break;
419 
420                               case Cfg_Byte:
421                                     *((unsigned char*)mv->DataPtr) =
422                                           (unsigned char)atoi(data);
423                                     ++retval;
424                                     break;
425 
426                               case Cfg_Ushort:
427                                     *((unsigned int*)mv->DataPtr) =
428                                           (unsigned int)atoi(data);
429                                     ++retval;
430                                     break;
431 
432                               case Cfg_Short:
433                                     *((int*)mv->DataPtr) = atoi(data);
434                                     ++retval;
435                                     break;
436 
437                               case Cfg_Ulong:
438                                     *((unsigned long*)mv->DataPtr) =
439                                           (unsigned long)atol(data);
440                                     ++retval;
441                                     break;
442 
443                               case Cfg_Long:
444                                     *((long*)mv->DataPtr) = atol(data);
445                                     ++retval;
446                                     break;
447 
448                               case Cfg_Double:
449                                     *((double*)mv->DataPtr) = atof(data);
450                                     ++retval;
451                                     break;
452 
453                               case Cfg_Boolean:
454                                     *((int*)mv->DataPtr) = 0;
455                                     data[0] = tolower(data[0]);
456                                     if (('y' == data[0]) || ('t' == data[0]))
457                                           *((int*)mv->DataPtr) = 1;
458                                     ++retval;
459                                     break;
460 
461                               case Cfg_I_Array:
462                               {
463                                     int *ip;
464                                     char *str;
465 
466                                     ip = ((int*)mv->DataPtr);
467                                     str = strtok(data, " ,\t");
468                                     while (NULL != str)
469                                     {
470                                           *ip = atoi(str);
471                                           ip++;
472                                           str = strtok(NULL, " ,\t");
473                                     }
474                                     ++retval;
475                                     break;
476                               }
477 
478                               default:
479 #ifdef TEST
480                                     printf("Unknown conversion type\n");
481 #endif
482                                     retval = -1;
483                                     break;
484                               }
485                         }
486                         if (-1 == retval)
487                               break;
488                   };
489 
490                   /*
491                   ** Variable wasn't found.  If we don't want it,
492                   ** then ignore it
493                   */
494             }
495             case NotInSection:
496                   break;  /* Not interested in this line */
497 
498             case NewSection:
499                   break;  /* Who cares? It's not our section */
500 
501             case FoundSection:
502                   break;  /* We found our section! */
503 
504             case LeavingSection:
505                   break;  /* We finished our section! */
506             }
507 
508             if (-1 == retval)
509                   break;
510       }
511 
512       if (ferror(CfgFile))
513       {
514             fclose(CfgFile);
515             return -2;
516       }
517       else
518       {
519             fclose(CfgFile);
520             return retval;
521       }
522 }
523 
524 /*
525 **  SearchCfg() - Search an .ini / .cfg file for a specific single datum.
526 **
527 **  Parameters: 1 - File name.
528 **              2 - Section name.
529 **              3 - Variable to find.
530 **              4 - Pointer to variable's storage.
531 **              5 - Type of vatiable.
532 **
533 **  Returns:  1 if succesful
534 **            0 if section/variable not found
535 **           -1 if invalid type spec
536 **           -2 for file error
537 */
538 
539 int SearchCfg(const char *FileName,
540               char *SectionName,
541               char *VarName,
542               void *DataPtr,
543               enum CfgTypes VarType
544              )
545 {
546       struct CfgStruct MyVars[2];
547 
548       MyVars[0].Name    = VarName;
549       MyVars[0].DataPtr = DataPtr;
550       MyVars[0].VarType = VarType;
551 
552       MyVars[1].Name = MyVars[1].DataPtr = NULL;
553 
554       return ReadCfg(FileName, SectionName, MyVars);
555 }
556 
557 /*
558 **  UpdateCfg() - This will update a variable in a specific section in your
559 **                .ini file. It will do so safely by copying it to a new file,
560 **                and when finished, will delete the old one and rename the
561 **                new one to the correct name. If any fatal error occurs, it
562 **                will return a -1 to indiate failure. I generally don't care
563 **                why it failed, just knowing that it failed is usually enough.
564 **
565 **  Paramters: 1 - File name.
566 **             2 - Section name.
567 **             3 - Variable tag.
568 **             4 - Pointer to textual representation of the variable's value.
569 **
570 **  Returns: -1 if a file error occurred, else 0.
571 **
572 **  Notes: 1. If the section doesn't yet exist in the file, it will be added.
573 **         2. If the variable doesn't yet exist in the file, it will be added.
574 **         3. New variables are created at the end of existing sections, or
575 **            on the last line before the first section name in the case where
576 **            the section name is "".
577 **         4. New sections are created at the end of the file.
578 */
579 
580 int UpdateCfg(const char *FileName,
581               char *SectionName,
582               char *VarWanted,
583               char *NewData)
584 {
585       FILE *CfgFile;
586       char line[BUFFERSIZE];
587       char SectionWanted[BUFFERSIZE];
588       char CurrentSection[BUFFERSIZE];
589       enum LineTypes linetype;
590       char var[BUFFERSIZE];
591       char data[BUFFERSIZE];
592       char TempFileName[FILENAME_MAX];
593       FILE *NewCfgFile;
594       int Error = 0;
595       int updated = 0;
596 
597       CfgFile = cant((char *)FileName, "r");
598 
599       strcpy(TempFileName, FileName);
600       chgext(TempFileName, NULL, "tmp");
601       NewCfgFile = cant(TempFileName, "w");
602 
603       strcpy(CurrentSection, "[]");
604       sprintf(SectionWanted, "[%s]", SectionName);
605 
606       while (EOF != ReadLine(CfgFile, line))
607       {
608             linetype = SectionLine(line, SectionWanted, CurrentSection);
609             switch (linetype)
610             {
611             case InSection:              /* In our section, parse it. */
612                   ParseLine(line, var, data);
613                   if ((StrEq(var, VarWanted)) && (!updated))
614                   {
615                         strncpy(data, NewData, BUFFERSIZE);
616                         data[BUFFERSIZE-1] = NUL;
617                         updated = 1;
618                   }
619                   fprintf(NewCfgFile, "%s = %s\n", var, data);
620                   break;
621 
622             case EmptyLine:         /* Fall Through.  Just copy it. */
623             case CommentLine:       /* Fall Through.  Just copy it. */
624             case NotInSection:      /* Fall Through.  Just copy it. */
625             case NewSection:        /* Fall Through.  Just copy it. */
626             case FoundSection:      /* Fall Through.  Just copy it. */
627                   fprintf(NewCfgFile, "%s\n", line);
628                   break;
629 
630             case LeavingSection:    /* Leaving section, may have to add it */
631                   if (!updated)     /* Variable wasn't found, we have      */
632                   {                 /* to add it.                          */
633                         fprintf(NewCfgFile, "%s = %s\n", VarWanted, NewData);
634                         updated = 1;
635                   }
636                   /*
637                   ** Now print current line
638                   */
639 
640                   fprintf(NewCfgFile, "%s\n", line);
641                   break;
642             }
643       }
644 
645       /*
646       ** Our section may not have even been there, in which case we have
647       ** to add both the variable and the section itself.
648       */
649 
650       if (!updated)
651       {     /* We may have hit EOF while still in our section. */
652             /* If so, we don't need to add the section header. */
653             if (!StrEq(CurrentSection, SectionWanted))
654                   fprintf(NewCfgFile, "%s\n", SectionWanted);
655             fprintf(NewCfgFile, "%s = %s\n", VarWanted, NewData);
656       }
657 
658       if (ferror(CfgFile))
659             Error = -1;
660       if (ferror(NewCfgFile))
661             Error = -1;
662       fclose(CfgFile);
663       fclose(NewCfgFile);
664 
665       if (!Error)
666       {
667             if (remove(FileName))
668                   return -1;
669             if (rename(TempFileName, FileName))
670                   return -1;
671       }
672       return Error;
673 }
674 
675 #ifdef TEST
676 
677 #include <stdlib.h>
678 
679 #if defined(MSDOS) || defined (_MSDOS_)
680  #define PINI_fname "prices.ini"
681 #else
682  #define PINI_fname "/home/mars_nwe/engr/disp/prices.ini"
683 #endif
684 
685 FILE *log_ = stderr;
686 
687 main()
688 {
689       char        Line[INI_LINESIZE];
690       int         Int;
691       long        Long;
692       double      Double;
693       Boolean_T   Bool;
694       struct      CfgStruct this_var;
695       FILE*       tst;
696       long        prices[2];
697 
698 
699       /*
700       **  First work with a test file
701       */
702       tst = cant("test.ini", "w");
703       fputs("[Section 1]\n", tst);
704       fputs("[Section 2]\n", tst);
705       fputs("[Section 3]\n", tst);
706       fputs("[Section 4]\n", tst);
707       fputs("[Section 5]\n", tst);
708       fputs("[Section 6]\n", tst);
709       fclose(tst);
710       puts("Updating the test configuration file");
711 
712       puts("Updating section 1");
713       UpdateCfg("test.ini", "Section 1", "string #1", "section 1 test");
714       puts("Updating section 2");
715       UpdateCfg("test.ini", "Section 2", "short #2", "2");
716       puts("Updating section 3");
717       UpdateCfg("test.ini", "Section 3", "long #3", "3");
718       UpdateCfg("test.ini", "Section 4", "double #4", "4.4");
719       UpdateCfg("test.ini", "Section 5", "boolean #5", "Y");
720       UpdateCfg("test.ini", "Section 6", "boolean #6", "N");
721       UpdateCfg("test.ini", "", "global string", "\"Hello, world!\" ;Comment");
722 
723       puts("I've finished the updates, now to try to get the data back");
724 
725       this_var.Name    = "global string";
726       this_var.DataPtr = Line;
727       this_var.VarType = Cfg_String;
728       printf("ReadCfg(0) returned %d; Line=\n",
729              ReadCfg("test.ini", "", &this_var));
730       puts(Line);
731 
732       this_var.Name    = "string #1";
733       this_var.DataPtr = Line;
734       this_var.VarType = Cfg_String;
735       printf("ReadCfg(1) returned %d; Line=\n",
736              ReadCfg("test.ini", "Section 1", &this_var));
737       puts(Line);
738 
739       this_var.Name    = "short #2";
740       this_var.DataPtr = ∬
741       this_var.VarType = Cfg_Short;
742       printf("ReadCfg(2) returned %d; Value= ",
743              ReadCfg("test.ini", "Section 2", &this_var));
744       printf("%d\n", Int);
745 
746       this_var.Name    = "long #3";
747       this_var.DataPtr = &Long;
748       this_var.VarType = Cfg_Long;
749       printf("ReadCfg(3) returned %d; Value = ",
750              ReadCfg("test.ini", "Section 3", &this_var));
751       printf("%ld\n", Long);
752 
753       this_var.Name    = "double #4";
754       this_var.DataPtr = &Double;
755       this_var.VarType = Cfg_Double;
756       printf("ReadCfg(4) returned %d; Value = ",
757              ReadCfg("test.ini", "Section 4", &this_var));
758       printf("%f\n", Double);
759 
760       this_var.Name    = "boolean #5";
761       this_var.DataPtr = &Bool;
762       this_var.VarType = Cfg_Boolean;
763       printf("ReadCfg(5) returned %d; Value = ",
764              ReadCfg("test.ini", "Section 5", &this_var));
765       printf("%c\n", Bool ? 'T' : 'F');
766 
767       this_var.Name    = "boolean #6";
768       this_var.DataPtr = &Bool;
769       this_var.VarType = Cfg_Boolean;
770       printf("ReadCfg(6) returned %d; Value = ",
771              ReadCfg("test.ini", "Section 6", &this_var));
772       printf("%c\n", Bool ? 'T' : 'F');
773 
774       /*
775       **  Look for non-existant sections and/or variables
776       */
777 
778       Line[0] = NUL;
779       this_var.Name    = "string #99";
780       this_var.DataPtr = Line;
781       this_var.VarType = Cfg_String;
782       printf("ReadCfg(99) returned %d; Line=\n",
783              ReadCfg("test.ini", "Section 99", &this_var));
784       puts(Line);
785 
786       Line[0] = NUL;
787       this_var.Name    = "string #99";
788       this_var.DataPtr = Line;
789       this_var.VarType = Cfg_String;
790       printf("ReadCfg(0/99) returned %d; Line=\n",
791              ReadCfg("test.ini", "", &this_var));
792       puts(Line);
793 
794       Line[0] = NUL;
795       this_var.Name    = "string #99";
796       this_var.DataPtr = Line;
797       this_var.VarType = Cfg_String;
798       printf("ReadCfg(1/99) returned %d; Line=\n",
799              ReadCfg("test.ini", "Section 1", &this_var));
800       puts(Line);
801 
802       Line[0] = NUL;
803       this_var.Name    = "string #1";
804       this_var.DataPtr = Line;
805       this_var.VarType = Cfg_String;
806       printf("ReadCfg(1/100) returned %d; Line=\n",
807              ReadCfg("test.ini", "Section 100", &this_var));
808       puts(Line);
809 
810       /*
811       **  Next, add a section and new variables
812       */
813 
814       UpdateCfg("test.ini", "", "new global variable", "abc");
815       UpdateCfg("test.ini", "Section -1", "new variable", "xyz");
816      
817       /*
818       **  Next work with a sample real PRICES.INI file
819       */
820 
821       this_var.Name    = "Price of #1";
822       this_var.DataPtr = &prices[0];
823       this_var.VarType = Cfg_Long;
824       printf("ReadCfg(1) returned %d; Value = ",
825              ReadCfg(PINI_fname, "", &this_var));
826       printf("%ld\n", prices[0]);
827 
828       this_var.Name    = "Price of #2";
829       this_var.DataPtr = &prices[1];
830       this_var.VarType = Cfg_Long;
831       printf("ReadCfg(2) returned %d; Value = ",
832              ReadCfg(PINI_fname, "", &this_var));
833       printf("%ld\n", prices[1]);
834 
835       UpdateCfg("prices.ini", "", "Price of #2", "999");
836 
837       this_var.Name    = "Price of #2";
838       this_var.DataPtr = &prices[1];
839       this_var.VarType = Cfg_Long;
840       printf("ReadCfg(2) returned %d; Value = ",
841              ReadCfg(PINI_fname, "", &this_var));
842       printf("%ld\n", prices[1]);
843 
844       UpdateCfg(PINI_fname, "", "Price of #2", "389");
845 
846       this_var.Name    = "Price of #2";
847       this_var.DataPtr = &prices[1];
848       this_var.VarType = Cfg_Long;
849       printf("ReadCfg(2) returned %d; Value = ",
850              ReadCfg(PINI_fname, "", &this_var));
851       printf("%ld\n", prices[1]);
852 
853       /*
854       **  Finally, try an invalid file name
855       */
856 
857       this_var.Name    = "global string";
858       this_var.DataPtr = Line;
859       this_var.VarType = Cfg_String;
860       printf("ReadCfg(0) returned %d; Line=\n",
861              ReadCfg("none.ini", "", &this_var));
862       puts(Line);
863 
864       return EXIT_SUCCESS;
865 }
866 
867 #endif
868