/***************************************************************************/
/*  Killer List Of Video-games Database.                                   */
/*  Copyright (C) 1993  John Keay                                          */
/*                                                                         */
/*  This program is free software; you can redistribute it and/or modify   */
/*  it under the terms of the GNU General Public License as published by   */
/*  the Free Software Foundation; either version 2 of the License, or      */
/*  (at your option) any later version.                                    */
/*                                                                         */
/*  This program is distributed in the hope that it will be useful,        */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
/*  GNU General Public License for more details.                           */
/*                                                                         */
/*  You should have received a copy of the GNU General Public License      */
/*  along with this program; if not, write to the Free Software            */
/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                         */
/*  For more details see 'COPYING'                                         */
/*  John Keay (keay@tiuk.ti.com)                                           */
/***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "proto.h"
#include "parse.h"

#ifndef TRUE
#define TRUE (!0)
#endif
#ifndef FALSE
#define FALSE 0
#endif

void *safe_malloc(size)
unsigned size;
{ void *tmp;

  tmp = (void *) malloc(size);
  if(tmp == NULL) {
    fprintf(stderr,"No MEMORY for malloc\n");
    exit(1);
  }
  return(tmp);
}

static char *error_name[] = { "WARNING", "ERROR" };

/* I usually use stdarg.h here, but given this makes it less portable  */
/*  and there isn't much error checking in the code anyway I'll take   */
/*  it out. */

static void parse_error PROTO((error_t e,parse_ptr p,char *mess));
static void parse_error(e,p,mess)
error_t e;
parse_ptr p;
char *mess;
{ char *ptr;
  int i;

  if(p) {
    fprintf(p->err,"%s: ",error_name[e]);
    fprintf(p->err,mess);
    fprintf(p->err,"\n");
    ptr = p->line;
    while(*ptr != '\0' && *ptr != '\n')
      fprintf(p->err,"%c",*ptr++);
    fprintf(p->err,"\n");
    for(i=0;i<p->token_pos;i++)
      fprintf(p->err," ");
    fprintf(p->err,"^\n");
  } else {
    fprintf(stderr,"%s: ",error_name[e]);
    fprintf(stderr,mess);
    fprintf(stderr,"\n");
  }
  if(e == E_FATAL)
    exit(1);
}

static void parse_get_char PROTO((parse_ptr p));
static void parse_get_char(p)
parse_ptr p;
{
  if(p->cptr && *p->cptr)
    p->cptr++;

  if(!p->cptr || !*p->cptr) {
    p->eof = !fgets(p->line,MAX_TOKEN_LEN,p->fp);
    p->cptr = p->line;
  }
}

static void parse_skip_white PROTO((parse_ptr p));
static void parse_skip_white(p)
parse_ptr p;
{
  while(*p->cptr == ' ' || *p->cptr == '\t')
    parse_get_char(p);
}

void parse_get_token(p)
parse_ptr p;
{ char *t = p->token;
  parse_skip_white(p);
  p->token_pos = (int) (p->cptr - p->line);
  if (isalnum(*p->cptr) || *p->cptr == '?' || *p->cptr == '\'' ||
      *p->cptr == '-' ||*p->cptr == '*') {
    do {
      *t++ = *p->cptr;
      parse_get_char(p);
    } while (isalnum(*p->cptr) || *p->cptr == '?' || *p->cptr == '.' ||
             *p->cptr == '\'' || *p->cptr == '-' || *p->cptr == '*');
  } else {
    *t++ = *p->cptr;
    parse_get_char(p);
  }
  *t++ = '\0';
}

parse_ptr parse_open(filename)
char *filename;
{ FILE *fp;
  parse_ptr p=NULL;

  if(!(fp = fopen(filename,"r"))) {
    char tmp[1024];  /* If only stdarg.h was standard */
    sprintf(tmp,"Can't open file \"%s\" for read",filename);
    parse_error(E_WARNING,NULL,tmp);
  } else {
    p = (parse_ptr) safe_malloc(sizeof(parse_t));

    p->filename = (char *) safe_malloc(strlen(filename)+1);
    p->line     = (char *) safe_malloc(MAX_TOKEN_LEN);
    p->token    = (char *) safe_malloc(MAX_LINE_LEN);
    strcpy(p->filename,filename);
    strcpy(p->line    ,"");
    strcpy(p->token   ,"");
    p->fp   = fp;
    p->cptr = NULL;    
    p->eof = FALSE;    
    p->token_pos = 0;  
    p->err = stderr;   
                       
    parse_get_char(p); 
    parse_get_token(p);
  }
  return(p);
}

void parse_close(p)
parse_ptr p;
{
  fclose(p->fp);
  free(p->filename);
  free(p->token);
  free(p->line);
  free(p);
}

int its(p,s)
parse_ptr p;
char *s;
{
 if (p->eof) {
   return(FALSE);
 } else if (!strcmp(s,p->token)) {
   parse_get_token(p);
   return(TRUE);
 }
 return(FALSE);
}

int its_not(p,s)
parse_ptr p;
char *s;
{
 if (p->eof) {
   return(FALSE);
 } else if (!strcmp(s,p->token)) {
   parse_get_token(p);
   return(FALSE);
 }
 return(TRUE);
}

void mustbe(p,s)
parse_ptr p;
char *s;
{ static char tmp[1024];

 if (p->eof) {     /* If only stdarg.h was standard */
   sprintf(tmp,"Expecting '%s' found EOF",s);
   parse_error(E_FATAL,p,tmp);
 } else if (strcmp(s,p->token)) {
   sprintf(tmp,"Expecting '%s' found '%s'",s,p->token);
   parse_error(E_FATAL,p,tmp);
 }
 parse_get_token(p);
}