summaryrefslogblamecommitdiffstats
path: root/tools/cpu/nios2/ptf.c
blob: db62e557b71392b0d1a44bb69fb3ccf1f305e64e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                           
                   









































                                                                             
                


























































































































































































                                                                             
       



























































































































                                                                                                       
                                                          






































































































                                                                                            
   








































                                                                              





































                                                                             





















                                                                 
                                  



                                          
                       









                                             
                               
         










                                                  





















































































                                                                               
       
























                                                                             
                   


















































                                                                             

                                





















                                                          
                                                                         





























































                                                                             
/*
 *  Copyright (c) 2006 Kolja Waschk rtemsdev/ixo.de
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 *
 *  $Id$
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "ptf.h"

#define PTFPARSER_MAXDEPTH 20
#define PTFPARSER_NAMEBUFSIZE 1024
#define PTFPARSER_VALUEBUFSIZE 4096

#define DEBUG_EXPECTATIONS 1
#define DEBUG_READVALUES 2
#define DEBUG_FINDER 4
#define DEBUG 0

struct ptf_parser_state
{
  struct ptf *tree;
  struct ptf *current_in[PTFPARSER_MAXDEPTH];
  struct ptf *current_item;

  long line;  /* starts with 1, increments whenever a LF (ASCII 10) passes */
  int section_level; /* starts at 0, incremented at {, decremented at } */
  char *filename;
  char name_buffer[PTFPARSER_NAMEBUFSIZE];
  int name_length;
  char value_buffer[PTFPARSER_VALUEBUFSIZE];
  int value_length;
  struct
  {
    unsigned error:1;
    unsigned escaped:1;
    unsigned single_quoted:1;
    unsigned double_quoted:1;
  } flag;

  enum
  {
    section_or_item_specification,
    more_section_type_or_item_name_chars,
    whitespace_after_section_specification,
    whitespace_after_section_or_item_name,
    whitespace_before_item_value,
    more_section_name_chars,
    more_item_value_chars,
  } expectation;
};

/***************************************************************************/
struct ptf *ptf_alloc_item(ptf_item_type t, char *name, char *value)
{
  struct ptf *new_item;
  new_item = (struct ptf *)malloc(sizeof(struct ptf));
  if(!new_item) return NULL;

  new_item->type = t;
  new_item->sub = NULL;
  new_item->next = NULL;
  new_item->name = NULL;
  new_item->value = NULL;

  if(name != NULL)
  {
    int n = strlen(name);
    if(n > 0)
    {
      new_item->name = (char *)malloc(n + 1);
      if(new_item->name == NULL)
      {
        free(new_item);
        return NULL;
      };
      strcpy(new_item->name, name);
    }
  };

  if(value != NULL)
  {
    int n = strlen(value);
    if(n > 0)
    {
      new_item->value = (char *)malloc(n + 1);
      if(new_item->value == NULL)
      {
        if(name != NULL) free(new_item->name);
        free(new_item);
        return NULL;
      };
      strcpy(new_item->value, value);
    };
  };

  return new_item;
}

/***************************************************************************/
void add_ptf_item(struct ptf_parser_state *state, struct ptf *item)
{
   if(state->current_item == NULL)
   {
     if(state->section_level > 0)
       state->current_in[state->section_level-1]->sub = item;
     else
       state->tree = item;
   }
   else
     state->current_item->next = item;
}


/***************************************************************************/
void parse_error(struct ptf_parser_state *state, char *errmsg)
{
  fprintf(stderr, "Error while parsing %s (line %lu): %s\n",
    state->filename, state->line, errmsg);

  state->flag.error = 1;
}

/***************************************************************************/
void init_parser(struct ptf_parser_state *state, char *filename)
{
  int i;

  state->line = 1;
  state->flag.error = 0;
  state->flag.escaped = 0;
  state->flag.double_quoted = 0;
  state->flag.single_quoted = 0;
  state->section_level = 0;

  state->filename = (char *)malloc(strlen(filename)+1);
  if(state->filename != NULL) strcpy(state->filename, filename);

  state->expectation = section_or_item_specification;

  state->tree = NULL;
  state->current_item = NULL;
  for(i=1; i<PTFPARSER_MAXDEPTH; i++) state->current_in[i] = NULL;
}

/***************************************************************************/
void ptf_free(struct ptf *ptf)
{
  struct ptf *this, *next;
  for(this = ptf; this != NULL; this = next)
  {
    next = this->next;
    if(this->value != NULL) free(this->value);
    if(this->name != NULL) free(this->name);
    if(this->type == section) ptf_free(this->sub);
    free(this);
  };
}

/***************************************************************************/
void abort_parsing(struct ptf_parser_state *state)
{
  if(state->filename != NULL) free(state->filename);
  ptf_free(state->tree);
}

/***************************************************************************/
int add_char_to_buffer(int *len, char *buf, int maxlen, char c)
{
  if(*len >= maxlen) return 0;

  buf[(*len)++] = c;

  return 1;
}

/***************************************************************************/
void parse_char(struct ptf_parser_state *state, int c)
{
  int is_not_quoted;
  int is_no_space;
  enum { item_parsed, section_opened, section_closed, none } parser_event;

  switch(c)
  {
    case '\\':
    {
      if(state->flag.escaped == 0)
      {
        state->flag.escaped = 1;
        return;
      };
      break;
    };
    case '"':
    {
      if(!state->flag.escaped && !state->flag.single_quoted)
      {
        state->flag.double_quoted = 1 - state->flag.double_quoted;
        return;
      }
      break;
    };
    case '\'':
    {
      if(!state->flag.escaped && !state->flag.double_quoted)
      {
        state->flag.single_quoted = 1 - state->flag.single_quoted;
        return;
      }
      break;
    };
    case '\n':
    {
      state->line++;
      break;
    };
    default:
      break;
  };

  parser_event = none;

  is_not_quoted = !(state->flag.escaped ||
    state->flag.single_quoted || state->flag.double_quoted);
  is_no_space = (!is_not_quoted || !isspace(c));
  state->flag.escaped = 0;

  switch(state->expectation)
  {
    case section_or_item_specification:
    {
#if DEBUG&DEBUG_EXPECTATIONS
      printf("Expectation: section_or_item_specification\n");
#endif

      if(is_not_quoted && c == '}')
      {
        parser_event = section_closed;
      }
      else if(is_no_space)
      {
        state->name_length = 1;
        state->name_buffer[0] = c;
        state->expectation = more_section_type_or_item_name_chars;
      };
      break;
    };

    case more_section_type_or_item_name_chars:
    {
#if DEBUG&DEBUG_EXPECTATIONS
      printf("Expectation: more_section_type_or_item_name_chars\n");
#endif

      /* Item name is stored in name_buffer */
      /* Section type is stored in name_buffer */
      if(is_no_space)
      {
        if(!add_char_to_buffer(&state->name_length, state->name_buffer, PTFPARSER_NAMEBUFSIZE, c))
          parse_error(state, "First word is too long; I expected a shorter section type or item name");
      }
      else
      {
        state->expectation = whitespace_after_section_or_item_name;
      }
      break;
    };

    case whitespace_after_section_specification:
    {
#if DEBUG&DEBUG_EXPECTATIONS
      printf("Expectation: whitespace_after_section_specification\n");
#endif

      if(c == '{')
        parser_event = section_opened;
      else if(is_no_space)
        parse_error(state, "Expected section content within brackets {...}");
      break;
    };

    case whitespace_after_section_or_item_name:
    {
#if DEBUG&DEBUG_EXPECTATIONS
      printf("Expectation: whitespace_after_section_or_item_name\n");
#endif

      if(c == '{')
      {
        state->value_length = 0;
        parser_event = section_opened;
      }
      else if(c == '=')
        state->expectation = whitespace_before_item_value;
      else if(is_no_space)
      {
        state->value_length = 1;
        state->value_buffer[0] = c;
        state->expectation = more_section_name_chars;
      };
      break;
    };

    case more_section_name_chars:
    {
#if DEBUG&DEBUG_EXPECTATIONS
      printf("Expectation: more_section_name_chars\n");
#endif

      /* Section name is stored in value_buffer */
      if(is_no_space)
      {
        if(!add_char_to_buffer(&state->value_length, state->value_buffer, PTFPARSER_VALUEBUFSIZE, c))
          parse_error(state, "Section name is too long");
      }
      else
        state->expectation = whitespace_after_section_specification;
      break;
    }

    case whitespace_before_item_value:
    {
#if DEBUG&DEBUG_EXPECTATIONS
      printf("Expectation: whitespace_before_item_value\n");
#endif

      if(is_not_quoted && c == ';')
      {
        state->value_length = 0;
        parser_event = item_parsed;
      }
      else if(is_no_space)
      {
        state->value_length = 1;
        state->value_buffer[0] = c;
        state->expectation = more_item_value_chars;
      };
      break;
    };

    case more_item_value_chars:
    {
#if DEBUG&DEBUG_EXPECTATIONS
      printf("Expectation: more_item_value_chars\n");
#endif

      /* Item value is stored in value_buffer */
      if(is_not_quoted && c == ';')
        parser_event = item_parsed;
      else if(is_no_space)
      {
        if(!add_char_to_buffer(&state->value_length, state->value_buffer, PTFPARSER_VALUEBUFSIZE, c))
          parse_error(state, "Item value is too long");
      }
      else
        parser_event = item_parsed;
      break;
    }

    default:
#if DEBUG&DEBUG_EXPECTATIONS
      printf("Expectation: %d (?)\n", state->expectation);
#endif

      parse_error(state, "Internal error: Unhandled state of expectation");
  };

  switch(parser_event)
  {
    /* TODO: pointer tuff */

    case item_parsed:
    {
      struct ptf *new_item;
      state->name_buffer[state->name_length] = 0;
      state->value_buffer[state->value_length] = 0;
#if DEBUG&DEBUG_READVALUES
      printf("== Item %s is '%s' ==\n", state->name_buffer, state->value_buffer);
#endif

      new_item = ptf_alloc_item(item, state->name_buffer, state->value_buffer);
      if(new_item == NULL)
      {
        parse_error(state, "Internal error: "
            "Could not allocate memory for new item");
        return;
      };

      add_ptf_item(state, new_item);
      state->current_item = new_item;
      state->expectation = section_or_item_specification;

      break;
    };
    case section_opened:
    {
      struct ptf *new_section;
      state->name_buffer[state->name_length] = 0;
      state->value_buffer[state->value_length] = 0;
#if DEBUG&DEBUG_READVALUES
      printf("== New %s section '%s' opened ==\n", state->name_buffer, state->value_buffer);
#endif

      if(state->section_level >= PTFPARSER_MAXDEPTH-1)
      {
        parse_error(state, "Internal error: "
             "cannot handle sections nested as deep as here.");
        return;
      };

      new_section = ptf_alloc_item(section, state->name_buffer, state->value_buffer);
      if(new_section == NULL)
      {
        parse_error(state, "Internal error: "
            "Could not allocate memory for new section");
        return;
      };

      add_ptf_item(state, new_section);
      state->current_item = NULL;
      state->current_in[state->section_level] = new_section;
      state->section_level++;

      state->expectation = section_or_item_specification;
      break;
    };
    case section_closed:
    {
      if(state->section_level < 1)
      {
        parse_error(state, "Found closing '}' without opening '{' before");
        return;
      };

      state->section_level--;
      state->current_item = state->current_in[state->section_level];
      state->expectation = section_or_item_specification;
#if DEBUG&DEBUG_READVALUES
      printf("-- Closed section --\n");
#endif
      break;
    };
    default:
      break;
  };
}

/***************************************************************************/
struct ptf *ptf_parse_file(char *filename)
{
  FILE *f;
  char buffer[1024];

  struct ptf *root;
  struct ptf_parser_state state;

  if(filename == NULL)
  {
    fprintf(stderr, "Internal error: "
                    "No filename was given to ptf_read()\n");
    return NULL;
  };

  f = fopen(filename, "r");
  if(f == NULL)
  {
    perror(filename);
    return NULL;
  };

  init_parser(&state, filename);

  while(!feof(f))
  {
    size_t r, n;

    if(ferror(f))
    {
      perror(filename);
      abort_parsing(&state);
      fclose(f);
      return NULL;
    };

    n = fread(buffer, 1, 1024, f);
    for(r=0; r<n && (state.flag.error==0); r++) parse_char(&state, buffer[r]);
  };

  fclose(f);

  if(state.section_level != 0)
  {
    parse_error(&state, "Input file seems to be incomplete, "
                        "one or more sections are not closed with '}'\n");
  };

  if(state.flag.error)
  {
    abort_parsing(&state);
    return NULL;
  };

  return state.tree;
}

/***************************************************************************/

struct ptf *ptf_concat(struct ptf *a, struct ptf *b)
{
  struct ptf *leaf = a;

  if(!a) return b;
  if(!b) return a;

  for(leaf = a; leaf->next != NULL; leaf = leaf->next);
  leaf->next = b;
  return a;
}

/***************************************************************************/

void ptf_dump_ptf_item(FILE *f, struct ptf_item *pi)
{
  int i;
  fprintf(f, "level=%d in %p\n", pi->level, pi);
  for(i=pi->level;i>=0;i--)
  {
    if(pi->item[i] != NULL)
    {
      fprintf(f, "  %d: %s name=%s value=%s\n",
        i,
        pi->item[i]->type == item ? "item":"section",
        pi->item[i]->name,
        pi->item[i]->value);
    }
    else
    {
        fprintf(f, "  %d: NULL\n");
    }
    fflush(f);
  }
}

/***************************************************************************/

void ptf_printf(FILE *s, struct ptf *tree, char *prefix)
{
  struct ptf *leaf;

  for(leaf = tree; leaf != NULL; leaf = leaf->next)
  {
    switch(leaf->type)
    {
      case section:
      {
        char *new_prefix;
        int new_prefix_len;
        new_prefix_len = strlen(prefix) + strlen(leaf->name) + 2;
        if(leaf->value != NULL && leaf->value[0] != 0)
        {
          new_prefix_len += strlen(leaf->value) + 1;
        };
        new_prefix = (char *)malloc(new_prefix_len);
        strcpy(new_prefix, prefix);
        strcat(new_prefix, leaf->name);
        if(leaf->value != NULL && leaf->value[0] != 0)
        {
          strcat(new_prefix, ":");
          strcat(new_prefix, leaf->value);
        };
        strcat(new_prefix, "/");
        fputs(new_prefix, s);
        fputs("\n", s);
        ptf_printf(s, leaf->sub, new_prefix);
        break;
      };

      case item:
      {
        char *c;
        fputs(prefix, s);
        fputs(leaf->name, s);
        fputs(" = \"", s);
        if(leaf->value == NULL)
        {
          fputs("(NULL)", s);
        }
        else
        {
          for(c=leaf->value; *c; c++)
          {
            if(*c=='\\' || *c=='"') putc('\\', s);
            putc(*c, s);
          };
        }
        fprintf(s, "\"\n");
        break;
      };

      default:
        break;
    };
  };
}

/***************************************************************************/

int ptf_advance_one(struct ptf_item *item)
{
  int d;
  struct ptf *leaf;

  d = item->level;
  leaf = item->item[d];

  if(leaf != NULL)
  {
    if(leaf->type == section && leaf->sub != NULL)
    {
      if(item->level >= MAX_SECTION_NESTING-1)
      {
        /* raise an error? hm, for now we silently ignore the subtree */
      }
      else
      {
        d++;
        item->item[d] = leaf->sub;
        item->level = d;
        return 0;
      }
    }
    item->item[item->level] = leaf->next;
  };

  while(item->item[d] == NULL)
  {
    if(d == 0)
    {
      item->level = 0;
      item->item[0] = NULL;
      errno = ENOENT;
      return -1;
    }
    d --;
    leaf = item->item[d];
    if(leaf != NULL) item->item[d] = leaf->next;
  };

  item->level = d;
  return 0;
}

/***************************************************************************/

int ptf_advance_until(
  struct ptf_item *item,
  ptf_item_type ttype,
  char *name,
  char *value)
{
  int r;
  struct ptf *leaf;

  do
  {
    leaf = item->item[item->level];
#if DEBUG&DEBUG_FINDER
    printf(" Does %s/%s match %s/%s?\n", leaf->name, leaf->value, name, value);
#endif

    if(leaf->type == ttype)
    {
      if(name == NULL)
      {
        if(value == NULL)
        {
          return 0; /* got it (any value) */
        }
        else if (leaf->value != NULL)
        {
          if(strcmp(leaf->value, value) == 0) return 0; /* got it */
        }
      }
      else if(leaf->name != NULL)
      {
        if(strcmp(leaf->name, name) == 0)
        {
          if(value == NULL)
          {
            return 0; /* got it (any value) */
          }
          else if(leaf->value != NULL)
          {
            if(strcmp(leaf->value, value) == 0) return 0; /* got it */
          }
        }
      }
    };
    r = ptf_advance_one(item);

  } while(r == 0);

  return r;
}

/***************************************************************************/

struct ptf *ptf_find(
  struct ptf *tree,
  struct ptf_item *item,
  ptf_item_type ttype,
  char *name,
  char *value)
{
  int r;

  if(item == NULL) { errno = EINVAL; return NULL; };
  if(tree == NULL) { errno = ENOENT; return NULL; };

  item->level = 0;
  item->item[0] = tree;

  if(ptf_advance_until(item, ttype, name, value) != 0) return NULL;

  r = item->level;
  item->level++; /* To match ptf_match */
  return item->item[r];
}

/***************************************************************************/

struct ptf *ptf_next(
  struct ptf_item *item,
  ptf_item_type ttype,
  char *name,
  char *value)
{
  int r;
  struct ptf *leaf;

  if(item == NULL) { errno = EINVAL; return NULL; };

  if(item->level < 1) return NULL;
  item->level--; /* To match ptf_match */

  r = ptf_advance_one(item);

  if(r == 0) r = ptf_advance_until(item, ttype, name, value);

  if(r != 0) return NULL;

  r = item->level;
  item->level++; /* To match ptf_match */
  return item->item[r];
}

/***************************************************************************/

int ptf_match(
  struct ptf *const ptf,
  struct ptf_item *const match,
  const ptf_match_action action,
  void *arg)
{
    int count;
    struct ptf *p;
    struct ptf_item pi;

    p = ptf;
    count = 0;
    pi.level = 0;

    while(p != NULL)
    {
        ptf_item_type mtype = match->item[pi.level]->type;
        char *mname = match->item[pi.level]->name;
        char *mvalue = match->item[pi.level]->value;

#if DEBUG&DEBUG_FINDER
        printf("Looking for %s/%s, checking %s/%s\n",
          mname, mvalue, p->name, p->value);
#endif

        if(mtype == p->type &&
           (mname==NULL || p->name==NULL || strcmp(mname, p->name)==0) &&
           (mvalue==NULL || p->value==NULL || strcmp(mvalue, p->value)==0))
        {
            pi.item[pi.level] = p;

            if(pi.level == match->level - 1)
            {
                if(action != NULL) action(&pi, arg);
                p = p->next;
                count++;
            }
            else
            {
                if(p->sub != NULL && pi.level < MAX_SECTION_NESTING-1)
                {
                    pi.item[pi.level] = p;
                    pi.level++;
                    p = p->sub;
                }
                else
                {
                    p = p->next;
                };
            };
        }
        else
        {
            p = p->next;
        };

        while(p == NULL && pi.level > 0)
        {
            pi.level--;
            p = pi.item[pi.level]->next;
        };
    };
    return count;
}

/***************************************************************************/

char *ptf_defused_name(char *orig_name)
{
  int i,j;
  char *s = (char *)malloc(1+strlen(orig_name));

  if(!s) return NULL;

  for(i=j=0;orig_name[i];i++)
  {
    if(!isalnum(orig_name[i]))
    {
      if(j>0) if(s[j-1]!='_') s[j++]='_';
    }
    else
    {
      s[j++] = toupper(orig_name[i]);
    };
  };
  s[j] = 0;
  return s;
}