The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * This code is a part of tux_perl, and is released under the GPL.
 * Copyright 2002 by Yale Huang<mailto:yale@sdf-eu.org>.
 * See README and COPYING for more information, or see
 *   http://tux-perl.sourceforge.net/.
 *
 * $Id: config_hash.c,v 1.2 2002/11/11 11:17:02 yaleh Exp $
 */

#include <stdio.h>
#include <string.h>
#include "config_hash.h"

#define MAX_LINE_LENGTH 1024

#define IS_BLANK(c) ((c)==' '||(c)=='\t')
#define IS_ALPHA(c) ( ((c)>='0'&&(c)<='9') || ((c)>='a'&&(c)<='z') || \
                      ((c)>='A'&&(c)<='Z') || ((c)=='_'))
#define PRINTABLE_STRING(s) ((s)==NULL?"<NULL>":(s))

int clear_node(hash_node *node)
{
  if(node==NULL)
    return 1;

  node->parent=NULL;
  node->prev=NULL;
  node->next=NULL;
  node->name=NULL;

  return 0;
}

hash_variable * new_variable(void)
{
  hash_variable *v=(hash_variable*)malloc(sizeof(hash_variable));
  clear_node(HASH_NODE(v));
  NODE_TYPE(v)=HASH_NODE_TYPE_VARIABLE;
  v->value=NULL;
  
  return v;
}

void free_node(hash_node * node)
{
  free(NODE_NAME(node));
}

void free_variable(hash_variable *var)
{
  free_node(HASH_NODE(var));
  free(var->value);
}

hash_folder * new_folder(void)
{
  hash_folder * f=(hash_folder*)malloc(sizeof(hash_folder));
  clear_node(HASH_NODE(f));

  NODE_TYPE(f)=HASH_NODE_TYPE_FOLDER;
  f->first_child=NULL;

  return f;
}

void free_folder(hash_folder * folder)
{
  hash_node *node,*next;

  if(folder == NULL)
    return;
  
  for(node=folder->first_child;node!=NULL;node=next){
    next=NODE_NEXT(node);
    switch(NODE_TYPE(node)){
    case HASH_NODE_TYPE_VARIABLE:
      free_variable(HASH_VARIABLE(node));
      break;
    case HASH_NODE_TYPE_FOLDER:
      free_folder(HASH_FOLDER(node));
      break;
    }
  }
  free_node(HASH_NODE(folder));
}
  

    
hash_node * append_child(hash_folder * folder, hash_node * node)
{
  hash_node *slibing;

  if(folder==NULL || node==NULL)
    return NULL;

  NODE_PARENT(node)=HASH_NODE(folder);
  NODE_NEXT(node)=NULL;
  if((slibing=folder->first_child)==NULL){
    folder->first_child=node;
    NODE_PREV(node)=NULL;
  }else{
    while(NODE_NEXT(slibing)!=NULL)
      slibing=NODE_NEXT(slibing);
    NODE_NEXT(slibing)=node;
    NODE_PREV(node)=slibing;
  }

  return node;
}

int parse_new_folder(char * line, char * name)
{
  char *p,*pname,*pend;

  if(line==NULL || name==NULL)
    return 0;

  for(p=line;IS_BLANK(*p);p++)
    ;

  if(*p!='<')
    return 0;

  p++;
  while(IS_BLANK(*p))
    p++;
  
  if(!IS_ALPHA(*p))
    return 0;

  pname=p;
  while(IS_ALPHA(*p))
    p++;
  pend=p;

  while(IS_BLANK(*p))
    p++;

  if(*p!='>')
    return 0;

  *pend='\0';
  strncpy(name,pname,MAX_LINE_LENGTH);

  return 1;
}

int parse_end_folder(char * line, char * name)
{
  char *p,*pname,*pend;

  if(line==NULL || name==NULL)
    return 0;

  for(p=line;IS_BLANK(*p);p++)
    ;

  if(*p!='<')
    return 0;

  p++;
  while(IS_BLANK(*p))
    p++;

  if(*p!='/')
    return 0;

  p++;
  while(IS_BLANK(*p))
    p++;

  if(!IS_ALPHA(*p))
    return 0;

  pname=p;
  while(IS_ALPHA(*p))
    p++;
  pend=p;

  while(IS_BLANK(*p))
    p++;

  if(*p!='>')
    return 0;  

  *pend='\0';
  if(strcmp(name,pname)==0)
    return 1;

  return 0;
}

int parse_variable(char * line, char * name, char * value)
{
  char *p,*pname,*pnameend,*pvalue,*pvalueend;

  if(line==NULL || name==NULL || value==NULL)
    return 0;

  for(p=line;IS_BLANK(*p);p++)
    ;

  if(!IS_ALPHA(*p))
    return 0;

  pname=p;
  while(IS_ALPHA(*p))
    p++;
  pnameend=p;

  if(!IS_BLANK(*p))
    return 0;

  while(IS_BLANK(*p))
    p++;

  pvalue=p;
  do{
    while(*p!='\0' && *p!='\r' && *p!='\n' && !IS_BLANK(*p))
      p++;
    pvalueend=p;
    while(IS_BLANK(*p))
      p++;
  }while(*p!='\0' && *p!='\r' && *p!='\n');

  *pnameend='\0';
  *pvalueend='\0';

  strncpy(name,pname,MAX_LINE_LENGTH);
  strncpy(value,pvalue,MAX_LINE_LENGTH);

  return 1;
}

hash_node * find_node(hash_node * first, const char *name)
{
  hash_node * p;

  if( first==NULL || name==NULL )
    return NULL;

  for(p=first;p!=NULL;p=NODE_NEXT(p)){
    if(NODE_NAME(p)!=NULL && strcmp(NODE_NAME(p),name)==0)
      return p;
  }

  return NULL;
}

hash_folder * read_hash(const char * filename)
{
  FILE * fp;
  hash_folder *root,*current_folder;
  hash_node *lash_slibing;
  char line[MAX_LINE_LENGTH+1],name[MAX_LINE_LENGTH+1],
    value[MAX_LINE_LENGTH+1];

  if(filename==NULL){
    fprintf(stderr,"Filename is NULL!\n");
    return NULL;
  }
  if((fp=fopen(filename,"r"))==NULL){
    fprintf(stderr,"Failed to open file: %s\n",filename);
    return NULL;
  }

  root=new_folder();

  current_folder=root;

  while(fgets(line,MAX_LINE_LENGTH,fp)!=NULL){
    if(parse_new_folder(line,name)){
      // new folder
      hash_folder *f=new_folder();
      NODE_NAME(f)=strdup(name);
      append_child(current_folder,HASH_NODE(f));
      current_folder=f;
    }else if(parse_variable(line,name,value)){
      // new variable
      hash_variable *v=new_variable();
      NODE_NAME(v)=strdup(name);
      v->value=strdup(value);
      append_child(current_folder,HASH_NODE(v));
    }else if(parse_end_folder(line,NODE_NAME(current_folder))){
      // close folder
      current_folder=HASH_FOLDER(NODE_PARENT(current_folder));
    }
  }

  fclose(fp);
  return root;
}

int dump_node(hash_node * node)
{
  hash_variable * var;
  hash_folder * folder;
  hash_node * child;

  if(node==NULL)
    return 1;
  switch(NODE_TYPE(node)){
  case HASH_NODE_TYPE_VARIABLE:
    var=HASH_VARIABLE(node);
    printf("VAR %s %s\n",PRINTABLE_STRING(NODE_NAME(var)),
	   PRINTABLE_STRING(var->value));
    break;
  case HASH_NODE_TYPE_FOLDER:
    folder=HASH_FOLDER(node);
    printf("FOLDER %s\n",PRINTABLE_STRING(NODE_NAME(folder)));
    for(child=folder->first_child;
	child!=NULL;
	child=NODE_NEXT(child))
      dump_node(child);
    printf("END %s\n",PRINTABLE_STRING(NODE_NAME(folder)));
    break;
  }
  return 0;
}

char * get_value(hash_folder * folder, const char * name)
{
  hash_node * node;

  if(folder==NULL || name==NULL)
    return NULL;

  for(node=folder->first_child;node!=NULL;node=NODE_NEXT(node))
    if(IS_VARIABLE(node) && strcmp(NODE_NAME(node),name)==0)
      return HASH_VARIABLE(node)->value;

  return NULL;
}