rendered paste body#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "main.h"
#include "debug.h"
#include "util.h"
#include "tconfig.h"
/* static sets the initial unchangeable structure */
struct tconfig_block *file_to_tconfig(struct tconfig_block *old, const char *filename)
{
FILE *cfile;
char *fbuf = NULL;
char *eip = NULL;
struct tconfig_block *block = NULL;
struct tconfig_block *head = NULL;
struct tconfig_block *search = NULL;
struct tconfig_block *tmp = NULL;
size_t size = 0;
size_t i = 0;
size_t count = 0;
/* For debug purposes */
int line = 1;
/* No memory allocated at this point, we don't need
* to die nicely
*/
if ((cfile = fopen(filename,"r")) == NULL)
{
fprintf(stderr,"Could not open configuration file: %s\n",filename);
exit(EXIT_FAILURE);
}
fbuf = tmalloc0(BUFFER_SIZE);
/* Read the entire file into memory */
for(i=0;(count = fread(&fbuf[i],1,1024,cfile)) == 1024;i+=1024)
{
if ((fbuf = realloc(fbuf,i+1024+1024)) == NULL)
{
troll_debug(LOG_FATAL,"Could not allocate memory for config file, barfing.\n");
exit(EXIT_FAILURE);
}
}
/* If NOT the end-of-file, we must have had an error of some sort, barf and die */
if (!feof(cfile))
{
fprintf(stderr, "An error occurred while reading the config file\n");
exit(EXIT_FAILURE);
}
/* Terminate it with a NULL */
fbuf[i+count] = '\0';
fclose(cfile);
if (old == NULL)
{
block = tmalloc(sizeof(struct tconfig_block));
block->parent = NULL;
block->child = NULL;
block->prev = NULL;
block->next = NULL;
block->key = NULL;
block->value = NULL;
head = block;
} else {
block = old;
/* Go to top left node */
while (block->parent != NULL || block->prev != NULL)
block = (block->parent != NULL) ? block->parent : block->next;
head = block;
}
eip = fbuf;
while(*eip)
{
switch (*eip)
{
case '{':
if (block->child != NULL)
{
block = block->child;
break;
}
block->child = tmalloc(sizeof(struct tconfig_block));
block->child->parent = block;
block = block->child;
block->child = NULL;
block->prev = NULL;
block->next = NULL;
block->key = NULL;
block->value = NULL;
break;
/* The very left element in tree should have the parent */
case '}':
/* Rewind the list the very left */
while (block->prev != NULL)
block = block->prev;
if (block->parent == NULL)
{
fprintf(stderr, "Mismatched } around line: %d\n",line);
exit(EXIT_FAILURE);
}
block = block->parent;
break;
/* Ugly comment parsing routines */
case '/':
if (*(eip+1) != '*' && *(eip+1) != '/')
{
fprintf(stderr,"Stray / on line: %d\n",line);
exit(EXIT_FAILURE);
}
/* If comment takes C form */
if (*(eip+1) == '*')
{
/* Skip over / and * */
eip+=2;
/* Go until terminating -> */
while (*eip != '\0')
{
if (*(eip+1) == '\0')
{
fprintf(stderr,"Reached end of file while looking for */\n");
exit(EXIT_FAILURE);
}
if (*eip == '*' && *(eip+1) == '/')
{
/* loop increases eip, so only add 1 */
eip++;
break; /* Found end of comment */
}
if (*eip == '\r' && *(eip+1) == '\n')
{
eip++;
line++;
}
else if (*eip == '\n')
{
line++;
}
else if (*eip == '\r')
{
line++;
}
eip++;
}
if (eip == '\0')
{
return head;
}
}
else /* // comment, go until \r\n, \n, or \r */
{
eip+=2; /* Skip over // */
while (*eip != '\0')
{
if (*eip == '\r')
{
/* This just means the file is over, not an error */
if (*(eip+1) == '\0')
return head;
if (*(eip+1) != '\n')
{
eip++;
line++;
break;
}
else
{
line++;
break;
}
}
else if (*eip == '\n')
{
line++;
break;
}
eip++;
}
if (*eip == '\0')
return head;
}
break;
case '\r':
if (*(eip+1) == '\n')
{
eip++;
line++;
}
else
line++;
if (eip == '\0')
return head;
break;
case '\n':
line++;
break;
case '\t':
break;
case ' ':
break;
default:
tmp = tmalloc(sizeof(struct tconfig_block));
/* Do a wasteful scan so I don't have to deal with realloc */
if (*eip == '"')
{
eip++; /* Skip the first " */
for (i=0;*(eip+i) != '\0' && *(eip+i) != '\n' && *(eip+i) != '\r' &&
*(eip+i) != '"';i++);
if (*(eip+i) != '"')
{
fprintf(stderr, "Unmatched \" on line %d\n",line);
exit(EXIT_FAILURE);
}
}
else
{
for (i=0;*(eip+i) != '\0' && *(eip+i) != ' ' && *(eip+i) != '\t';i++);
if (*(eip+i) != ' ' && *(eip+i) != '\t')
{
fprintf(stderr, "Unmatched key on line %d\n",line);
exit(EXIT_FAILURE);
}
}
size = i;
tmp->key = tmalloc0(size+1);
for (i=0;i<size;i++)
{
*(tmp->key+i) = *(eip++);
}
/* Skip over trailing " if exists, this could be a problem if key is next to value */
if (*eip == '"')
eip++;
/* Skip over whitespace, print error if EOF or there's a newline reached before text */
while(*eip == '\t' || *eip == ' ' || *eip == '\r' || *eip == '\n' || *eip == '\0')
{
if (*eip == '\0')
{
fprintf(stderr, "Reached end of file looking for %s's value on line %d\n",block->key,line);
exit(EXIT_FAILURE);
}
if (*eip == '\r' || *eip == '\n')
{
fprintf(stderr, "Reached end of line looking for %s's value on line %d\n",block->key,line);
exit(EXIT_FAILURE);
}
eip++;
}
/* Now to scan the value */
/* Do a wasteful scan so I don't have to deal with realloc */
if (*eip == '"')
{
eip++; /* Skip the first " */
for (i=0;*(eip+i) != '\0' && *(eip+i) != '\n' && *(eip+i) != '\r' &&
*(eip+i) != '"';i++);
if (*(eip+i) != '"')
{
fprintf(stderr, "Unmatched \" on line %d\n",line);
exit(EXIT_FAILURE);
}
}
else
{
for (i=0;*(eip+i) != '\0' && *(eip+i) != ' ' && *(eip+i) != '\t' &&
*(eip+i) != '\r' && *(eip+i) != '\n';i++);
if (*(eip+i) != ' ' && *(eip+i) != '\t' && *(eip+i) != '\r' && *(eip+i) != '\n')
{
fprintf(stderr, "Unmatched key on line %d\n",line);
exit(EXIT_FAILURE);
}
}
size = i;
tmp->value = tmalloc0(size+1);
for (i=0;i<size;i++)
{
*(tmp->value+i) = *(eip++);
}
tmp->value[size] = '\0';
/* Now that we've got all the values, we check for dupes
* to link more than one tconfig file. It might make more
*/
/* Rewind list */
search = block;
while (search->prev != NULL)
search = search->prev;
while (search != NULL)
{
if (search->key != NULL && search->value != NULL)
if (!strcmp(search->key,tmp->key) && !strcmp(search->value,tmp->value))
{
free(tmp->key);
free(tmp->value);
free(tmp);
tmp = NULL;
block = search;
break;
}
block = search;
search = search->next;
}
if (tmp != NULL)
{
if (block->key == NULL && block->value == NULL)
{
block->key = tmp->key;
block->value = tmp->value;
/* Keeping above memory */
free(tmp);
}
else
{
/* Make a new block */
block->next = tmp;
tmp->prev = block;
tmp->next = NULL;
tmp->parent = NULL;
tmp->child = NULL;
block = block->next;
}
}
break;
}
eip++; /* While loop increase */
}
return head;
}
void free_tconfig_r(struct tconfig_block *tcfg)
{
if (tcfg->child)
free_tconfig_r(tcfg->child);
if (tcfg->next)
free_tconfig_r(tcfg->next);
if (tcfg->key != NULL)
free(tcfg->key);
if (tcfg->value != NULL)
free(tcfg->value);
free(tcfg);
}
void free_tconfig(struct tconfig_block *tcfg)
{
struct tconfig_block *tmp;
if (tcfg == NULL)
return;
tmp = tcfg;
/* Rewind to the top left, it should be there already */
while (tmp->prev != NULL && tmp->parent != NULL)
tmp = (tmp->parent) ? tmp->parent : tmp->prev;
free_tconfig_r(tcfg);
return;
}
static void tconfig_r_depth_first_write(int depth, FILE *out, struct tconfig_block *tmp)
{
int i;
if (tmp == NULL)
return;
if (tmp->child != NULL)
{
for(i=0;i<(depth*2);i++)
fputc(' ',out);
fprintf(out,"\"%s\"\t\"%s\"\n",tmp->key,tmp->value);
for(i=0;i<(depth*2);i++)
fputc(' ',out);
fprintf(out,"{\n");
tmp = tmp->child;
depth++;
}
else if (tmp->next != NULL)
{
for(i=0;i<(depth*2);i++)
fputc(' ',out);
fprintf(out,"\"%s\"\t\"%s\"\n",tmp->key,tmp->value);
tmp = tmp->next;
}
else
{
for(i=0;i<(depth*2);i++)
fputc(' ',out);
fprintf(out,"\"%s\"\t\"%s\"\n",tmp->key,tmp->value);
if (depth > 0)
{
while (tmp)
{
if (tmp->next == NULL)
{
if (depth == 0)
return;
while (tmp->parent == NULL)
{
tmp = tmp->prev;
}
tmp = tmp->parent;
depth--;
for(i=0;i<(depth*2);i++)
fputc(' ',out);
fprintf(out,"}\n\n");
}
else
{
tmp = tmp->next;
break;
}
}
}
else
return;
}
tconfig_r_depth_first_write(depth,out,tmp);
}
/* write a tconfig block back out */
void tconfig_to_file(struct tconfig_block *tcfg, char *filename)
{
FILE *out;
struct tconfig_block *tmp = tcfg;
struct tconfig_block *search;
int indent;
if (tmp == NULL)
{
troll_debug(LOG_WARN,"tconfig_to_file() passed a NULL tconfig block.");
return;
}
if ((out = fopen(filename,"w")) == NULL)
{
troll_debug(LOG_ERROR,"Could not open %s for writing.",filename);
return;
}
tconfig_r_depth_first_write(0,out,tmp);
fclose(out);
}