/*
 * Copyright (C) 2002-2014 Free Software Foundation, Inc.
 *
 * This file is part of LIBTASN1.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */


/*****************************************************/
/* File: Test_parser.c                               */
/* Description: Test sequences for these functions:  */
/*     asn1_parser_asn1,                             */
/*****************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "libtasn1.h"

typedef struct
{
  int lineNumber;
  const char *line;
  int errorNumber;
  const char *errorDescription;
} test_type;

const char *fileCorrectName;
char fileErroredName[] = "Test_parser_ERROR.asn";

#define _FILE_ "Test_parser_ERROR.asn"

test_type test_array[] = {
  /* Test DEFINITIONS syntax */
  {5,
   "TEST_PARSER2 { } DEFINITIONS IMPLICIT TAGS ::= BEGIN int1 ::= INTEGER END",
   ASN1_SYNTAX_ERROR,
   _FILE_
   ":6: Error: syntax error, unexpected IDENTIFIER, expecting $end near 'TEST_PARSER'"},
  {6, "TEST_PARSER { }", ASN1_SUCCESS, ""},

  /* Test ASN1_MAX_NAME_SIZE (128) */
  {12,
   "a123456789012345678901234567890123456789012345678901234567890123 ::= INTEGER",
   ASN1_SUCCESS, ""},
  {12,
   "a1234567890123456789012345678901234567890123456789012345678901234 ::= INTEGER",
   ASN1_NAME_TOO_LONG,
   _FILE_ ":12: name too long (more than 64 characters)"},
  /* Test 'check identifier' function */
  {12, "ident1 ::= ident2   ident2 ::= INTEGER",
   ASN1_SUCCESS, ""},
  {12, "ident1 ::= ident2",
   ASN1_IDENTIFIER_NOT_FOUND, _FILE_ ":: identifier 'ident2' not found"},
  {12, "obj1 OBJECT IDENTIFIER ::= {pkix 0 5 4}    "
   "pkix OBJECT IDENTIFIER ::= {1 2}",
   ASN1_SUCCESS, ""},
  {12, "obj1 OBJECT IDENTIFIER ::= {pkix 0 5 4}",
   ASN1_IDENTIFIER_NOT_FOUND, _FILE_ ":: identifier 'pkix' not found"},

  /* Test INTEGER */
  {14, "int1 INTEGER (-5..5),", ASN1_SUCCESS, ""},
  {14, "int1 INTEGER OPTIONAL,", ASN1_SUCCESS, ""},
  {14, "int1 INTEGER DEFAULT 1,", ASN1_SUCCESS, ""},
  {14, "int1 INTEGER DEFAULT -1,", ASN1_SUCCESS, ""},
  {14, "int1 INTEGER DEFAULT v1,", ASN1_SUCCESS, ""},
  {14, "int1 [1] INTEGER,", ASN1_SUCCESS, ""},
  {14, "int1 [1] EXPLICIT INTEGER,", ASN1_SUCCESS, ""},
  {14, "int1 [1] IMPLICIT INTEGER,", ASN1_SUCCESS, ""},
  {12, "Integer ::= [1] EXPLICIT INTEGER {v1(-1), v2(1)}", ASN1_SUCCESS, ""},
  {12, "Integer ::= INTEGER {v1(0), v2}", ASN1_SYNTAX_ERROR,
   _FILE_ ":12: Error: syntax error, unexpected '}', expecting '(' near '}'"},
  {12, "Integer ::= INTEGER {v1(0), 1}",
   ASN1_SYNTAX_ERROR,
   _FILE_
   ":12: Error: syntax error, unexpected NUM, expecting IDENTIFIER or '(' near '1'"},
  {12, "const1 INTEGER ::= -1", ASN1_SUCCESS, ""},
  {12, "const1 INTEGER ::= 1", ASN1_SUCCESS, ""},
  {12, "const1 INTEGER ::= v1",
   ASN1_SYNTAX_ERROR,
   _FILE_
   ":12: Error: syntax error, unexpected IDENTIFIER, expecting NUM or '+' or '-' near 'v1'"},
  {16, " generic generalstring",
   ASN1_IDENTIFIER_NOT_FOUND,
   _FILE_ ":: identifier 'generalstring' not found"},

  /* Test: OID */
  {20, "   oid1    OBJECT IDENTIFIER DEFAULT Oid-type",
   ASN1_IDENTIFIER_NOT_FOUND, _FILE_ ":: identifier 'Oid-type' not found"},
  {20, "   oid1    OBJECT IDENTIFIER DEFAULT 1",
   ASN1_IDENTIFIER_NOT_FOUND, _FILE_ ":: identifier '1' not found"},
  {20, "   oid1    OBJECT IDENTIFIER DEFAULT",
   ASN1_SYNTAX_ERROR,
   _FILE_ ":21: Error: syntax error, unexpected '}' near '}'"},
  {20, "   oid1    OBJECT IDENTIFIER DEFAULT Oid-type1",
   ASN1_SUCCESS, ""},

  /* end */
  {0}
};

static int
readLine (FILE * file, char *line)
{
  int c;

  while (((c = fgetc (file)) != EOF) && (c != '\n'))
    {
      *line = c;
      line++;
    }

  *line = 0;

  return c;
}

static void
createFile (int lineNumber, const char *line)
{
  FILE *fileIn, *fileOut;
  char lineRead[1024];
  int fileInLineNumber = 0;

  fileIn = fopen (fileCorrectName, "r");
  fileOut = fopen (fileErroredName, "w");

  while (readLine (fileIn, lineRead) != EOF)
    {
      fileInLineNumber++;
      if (fileInLineNumber == lineNumber)
	fprintf (fileOut, "%s\n", line);
      else
	fprintf (fileOut, "%s\n", lineRead);
    }

  fclose (fileOut);
  fclose (fileIn);
}


int
main (int argc, char *argv[])
{
  int result;
  asn1_node definitions = NULL;
  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
  test_type *test;
  int errorCounter = 0, testCounter = 0;
  int verbose = 0;

  if (argc > 1)
    verbose = 1;

  fileCorrectName = getenv ("ASN1PARSER");
  if (!fileCorrectName)
    fileCorrectName = "Test_parser.asn";

  if (verbose != 0)
    {
      printf ("\n\n/****************************************/\n");
      printf ("/*     Test sequence : Test_parser      */\n");
      printf ("/****************************************/\n\n");
      printf ("ASN1PARSER: %s\n", fileCorrectName);
    }

  result = asn1_parser2tree (fileCorrectName, &definitions, errorDescription);

  if (result != ASN1_SUCCESS)
    {
      printf ("File '%s' not correct\n", fileCorrectName);
      asn1_perror (result);
      printf ("ErrorDescription = %s\n\n", errorDescription);
      exit (1);
    }

  /* Only for Test */
  /* asn1_visit_tree(stdout,definitions,"TEST_PARSER",ASN1_PRINT_ALL); */

  /* Clear the definitions structures */
  asn1_delete_structure (&definitions);


  test = test_array;

  while (test->lineNumber != 0)
    {
      testCounter++;

      createFile (test->lineNumber, test->line);

      result =
	asn1_parser2tree (fileErroredName, &definitions, errorDescription);
      asn1_delete_structure (&definitions);

      if ((result != test->errorNumber) ||
	  (strcmp (errorDescription, test->errorDescription)))
	{
	  errorCounter++;
	  printf ("ERROR N. %d:\n", errorCounter);
	  printf ("  Line %d - %s\n", test->lineNumber, test->line);
	  printf ("  Error expected: %s - %s\n",
		  asn1_strerror (test->errorNumber), test->errorDescription);
	  printf ("  Error detected: %s - %s\n\n", asn1_strerror (result),
		  errorDescription);
	  exit (1);
	}

      test++;
    }


  if (verbose != 0)
    {
      printf ("Total tests : %d\n", testCounter);
      printf ("Total errors: %d\n", errorCounter);
    }

  if (errorCounter > 0)
    return 1;

  exit (0);
}
