Parsowanie plików konfiguracyjnych
-- Sebastian Pawlak, 2005.
Przedstawione funkcje umożliwiają dokonywanie operacji na prostych plikach konfiguracyjnych.
Kod źródłowy pliku "confParser.h":
/* confParser.h: biblioteka funkcji pozwalajacych odczytac i modyfikowac * pliki konfiguracyjne. * * Sebastian Pawlak, 2005. */ #ifndef _CONF_PARSER_H_ #define _CONF_PARSER_H_ /* dopuszczalne parametry pliku konfiguracyjnego */ enum { MAX_LINE = 255, /* max. rozmiar wiersza */ MAX_IDENT = 32, /* max. dlugosc identyfikatora */ MAX_VAL = 64 /* max. dlugosc wartosci */ }; typedef struct ident { char *ident; /* nazwa identyfikatora w pliku konfiguracyjnym */ char type; /* typ zmiennej: s - char[], i - int */ void *var; /* wskaznik na zmienna, do ktorej przypisac/skopiowac */ } ident; int parseConf(const char *fileName, const ident idns[]); int exchangeConf(const char *fileName, const char *ident, const char *val); #endif
Kod źródłowy pliku "confParser.c":
/* confParser.c: biblioteka funkcji pozwalajacych odczytac i modyfikowac * pliki konfiguracyjne. * Plik konfiguracyjny moze miec postac, jak na przykladzie: * * # komentarz * zmiennaLiczbowa 10 # komentarz * * zmiennaTekstowa "cogito ergo sum" * * Sebastian Pawlak, 2005. */ #include <stdio.h> #include <unistd.h> /* dla close() */ #include <string.h> /* dla strcmp(), strcpy() */ #include <stdlib.h> /* dla strtol() */ #include <fcntl.h> /* O_RDONLY */ #include "confParser.h" /* parseConf: odczytuje z pliku dane konfiguracyjne i przypisuje * wartosci odpowiednim zmiennym. * Wiersz pliku ma stanowic pare: identyfikator wartosc * Identyfikator musi byc oddzielony od wartosci conajmniej jednym * bialym znakiem. * Poszczegolne pary musza byc w kolejnych wierszach. * Komentarz rozpoczyna sie znakiem # i konczy wraz z koncem * wiersza. * Max. dlugosc wiersza = MAX_LINE * Max. dlugosc identyfikatora = MAX_IDENT * Max. dlugosc wartosci = MAX_VAL */ int parseConf(const char *fileName, const ident idns[]) { int i; FILE *f; char *p, *q; char s[MAX_LINE + 1], t[16], ident[MAX_IDENT + 1], val[MAX_VAL]; if ((f = fopen(fileName, "r")) == NULL) return -1; /* tworzy string postaci: %16s %64s */ sprintf(t, "%%%ds %%%ds", MAX_IDENT, MAX_VAL); while (fgets(s, MAX_LINE, f) != NULL) { /* kolejny wiersz */ /* jesli nie odczytano dwoch tokenow lub mamy komentarz ... */ if ((sscanf(s, t, ident, val) != 2) || (ident[0] == '#') || (val[0] == '#')) continue; /* jesli wartosc ujeta jest w cudzyslowy */ if ((val[0] == '\"') && ((p = strchr(s, '\"')) != NULL) && /* pierwszy cudzyslow */ ((q = strchr(p + 1, '\"')) != NULL)) { /* zamykajacy */ strncpy(val, p + 1, q - p - 1); val[q - p - 1] = '\0'; } /* przypisywanie zmiennym wartosci odczytanych z pliku */ for (i = 0; idns[i].ident != NULL; i++) if (!strcmp(idns[i].ident, ident)) switch (idns[i].type) { case 's': /* string */ strcpy(idns[i].var, val); break; case 'i': /* integer */ *((int *)idns[i].var) = strtol(val, NULL, 10); break; default: fprintf(stderr, "Bledny typ parametru!\n"); fclose(f); return -1; break; } } fclose(f); return 0; } /* exchangeConf: modyfikuje wartosc dla podanego identyfikatora w pliku * konfiguracyjnym. * Wczytuje plik do pamieci, modyfikuje wartosc, zapisuje plik. * Wartosc moze miec dlugosc wieksza/mniejsza niz dlugosc * poprzedniej wartosci. */ int exchangeConf(const char *fileName, const char *ident, const char *val) { long int i = 0, siz; int f, j = 0, isComment = 0, isIdent = 0; unsigned char *buf = NULL, c, *p = NULL; if ((f = open(fileName, O_RDONLY, 0)) == -1) return -1; if ((siz = lseek(f, 0, 2)) == -1) /* rozmiar pliku -- lseek na koniec */ return -2; if (lseek(f, 0, 0) == -1) /* spowrotem na poczatek pliku */ return -3; if ((buf = (unsigned char *)malloc(siz + 1)) == NULL) /* pamiec */ return -4; if ((siz = read(f, buf, siz)) == -1) /* wczytywanie pliku */ return -5; buf[siz] = '\0'; close(f); if ((f = open(fileName, O_WRONLY | O_TRUNC, 0)) == -1) return -6; while (i < siz) { c = buf[i++]; /* znak z bufora */ if (c == '#') /* komentarz */ isComment = 1; else if (c == '\n') /* koniec komentarza (nowy wiersz) */ isComment = 0, isIdent = 0, j = 0; else if (isComment == 0) { if (isIdent == 0) { /* nie-identyfikator */ if (c == ident[j]) { /* kolejny znak sie zgadza */ j++; if (j == strlen(ident)) { /* caly ident sie zgadza */ j = 0; /* jesli nastepny znak po 'c' to bialy znak */ if ((buf[i] == ' ') || (buf[i] == '\t')) isIdent = 1; } } else j = 0; } else { /* isIdent == 1 identyfikator */ if ((c != ' ') && (c != '\t')) { /* poczatek wartosci */ write(f, val, strlen(val)); /* zapis nowej wartosci */ /* ominiecie starej wartosci w buforze */ if ((p = strpbrk(&buf[i], " \n\t")) != NULL) i += p - &buf[i]; else i = siz; isIdent = 0; continue; } } } write(f, &c, 1); /* zapis znaku do pliku */ } close(f); return 0; }
Kod źródłowy pliku "main.c":
#include <stdio.h> #include "confParser.h" int main(void) { int zmienna = 0, cogito = 0; char ergo[MAX_VAL + 1] = ""; const ident parametry[] = { { "zmienna", 'i', &zmienna }, { "cogito", 'i', &cogito }, { "ergo", 's', ergo }, { NULL } }; /* odczyt parametrow z pliku konfiguracyjnego "plik.conf" */ if (parseConf("plik.conf", parametry) != 0) { fprintf(stderr, "Blad odczytu konfiguracji z pliku!\n"); exit(-1); } fprintf(stdout, "zmienna: %d\n", zmienna); fprintf(stdout, "cogito: %d\n", cogito); fprintf(stdout, "ergo: \"%s\"\n", ergo); return 0; }