Antes de falarmos sobre qual estrutura de dados criar é importante ter em mente que não existe resposta certa ou errada para o que estou propondo. O fato é que a solução para a descrição de um arquivo ini vai depender da experiência do programador, do seu conhecimento e da sua prática. A estrutura de dados pode ficar complexa, dependendo do tipo de operação que se deseje executar. É importante ter isso em mente pois cada pessoa tem um ponto-de-vista significativamente distinto quando avaliando um problema e várias soluções podem surgir.
Se a solução é certa ou errada vai depender de fatores como tempo de desenvolvimento, recursos necessários para a implementação, ambiente de execução e outras restrições de projeto que podem surgir ao longo do caminho. O que quero chamar a atenção é que deve-se ouvir todas as proposições de solução antes de decidir qual será adotada.
Como critério, tenho a tendência de usar a mais fácil de ser implementada. Normalmente, soluções simples levam a código simples, compacto e eficiente. Bem, chega de conversa. Vamos à definição das estruturas de dados.
Para o usuário da biblioteca, é importante que ele tenha em mãos um descritor do arquivo de configurações. Este descritor permitirá que a biblioteca seja utilizada com diversos arquivos distintos. No entanto, queremos que o usuário da biblioteca use somente nossas funções para manipular os dados em memória. Assim, o descritor deverá ser algo opaco, declarado dessa forma:
typedef struct ini_cfg_file_t ini_cfg_file_t;
Esta declaração deverá ser criada dentro do arquivo de cabeçalho público da aplicação. Note que não definimos o conteúdo da estrutura. Isso, na verdade, é um truque para forçar o compilador a verificar os tipos sem, no entanto, revelar o conteúdo da estrutura. Obviamente que quando escrevermos as funções que manipulam esta estrutura, definiremos os campos da estrutura. Afinal, o compilador não é adivinho.
O conteúdo de um arquivo ini é claramente hierárquico, ou seja, há pares chave-valor dentro de uma seção. Assim, poder-se-ia descrever a estrutura de um arquivo ini como sendo chaves e valores e, por fim, seções:
typedef struct {
const char *key;
const char *value;
} ini_cfg_keypair_t;
typedef struct {
const char *section;
ini_cfg_keypair_t *pairs;
} ini_cfg_section_t;
Aqui definimos duas estruturas: uma para descrever os pares chave-valor e outra para descrever uma seção. A seção é composta por um nome e depois por um vetor de pares. Dessa forma temos uma maneira extremamente simples de descrever as informações. A única coisa que nos falta descrever é o arquivo em si, o descritor ini_cfg_file_t:
typedef struct {
ini_cfg_section_t *sections;
} ini_cfg_file_t;
A descrição do arquivo deixa clara a principal intenção do código: manter todo o arquivo ini em memória. Esta abordagem pode não ser boa se o arquivo ini for muito grande. No entanto, considerando que um arquivo de configurações não ultrapassa mais do que alguns quilobytes, esta abordagem é válida.
Em um projeto que desenvolvi para um cliente foi necessária a existência de algum arquivo de configuração da aplicação. Como não consegui achar nenhuma biblioteca pronta que fosse confiável – encontrei um projeto open source que parecia engenharia de foguete – resolvi escrever minha própria rotina tendo em mente a simplicidade e a performance. Por questões contratuais, não posso reproduzir aqui o código daquela biblioteca, mas posso escrever um código novo, mais eficiente e com algumas premissas distintas.
Bem, este é o primeiro artigo de uma série que discutirá essas rotinas. O código-fonte pretendo publicar no Source Forge como um pequeno projeto open source para quem quiser usar em seus sistemas. O projeto da biblioteca usará apenas STD C99, sem frescuras e com dependência apenas com relação à biblioteca STDC, conforme padronizada pelo ANSI. Assim, garante-se que o código final será totalmente portável.
Como artigo introdutório, falemos sobre os arquivos de configuração. Logo nos primórdios do Windows alguém teve uma ideia, brilhante diga-se de passagem, de descrever configurações como pares chave/valor, organizados por seções, em arquivos de texto plano. Essa abordagem permite que os arquivos sejam facilmente editados por um editor de textos qualquer e que contenham informações inteligíveis para seres humanos. O problema dessa abordagem está no tamanho do arquivo de configurações, que pode ser tão grande quanto se queira.
O fato é que toda abordagem tem suas limitações. Para o quadro geral eu diria que um arquivo no formato ini é mais do que adequado para a grande maioria dos projetos, podendo ser usado com simplicidade. Linguagens como o PHP utilizam arquivos ini como meio de armazenagem das configurações do seu run-time. Os arquivos ini são simples, fáceis de serem lidos e interpretados. E permitem que sua aplicação tenha uma forma de configuração suficientemente completa para a quase totalidade das suas necessidades de persistência de configurações.
; comentário
[seção]
chave1 = valor1
chave2 = valor2
chave3 = valor3
chave1 = valor4 ; Repetiu-se
Os comentários são iniciados pelo ponto-e-vírgula e são comentários de linha, ou seja, invalidam a interpretação do que quer que esteja à sua direita dentro de uma linha. Os conjuntos “chave/valor” são organizados por seções que funcionam como uma forma simples, e muito eficiente, de controlar o acesso ao espaço de nomes global. Em verdade, as seções criam espaços de nomes reduzidos. Os pares “chave/valor” são os itens de configuração. Note-se que se uma chave/valor for redefinida, será o valor da redefinição que será utilizado pela biblioteca.
De posse dessas restrições já podemos criar as estruturas de dados que serão usadas pela biblioteca. Por uma questão de design, a biblioteca será orientada ao objeto, mesmo que a linguagem C não permita que isso seja realizado sintaticamente. No entanto, nada impede que o façamos semanticamente. Aqui é importante ter em mente o seguinte: não force uma linguagem a fazer algo que ela não foi projetada. É possível programar orientado ao objeto em C, mas é importante ter em mente que não é uma boa ideia tentar forças a sintaxe declarativa de linguagens que suportam de facto a orientação ao objeto.

