/*
** Splint - annotation-assisted static program checker
** Copyright (C) 1994-2003 University of Virginia,
**         Massachusetts Institute of Technology
**
** 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.
** 
** The GNU General Public License is available from http://www.gnu.org/ or
** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
** MA 02111-1307, USA.
**
** For information on splint: info@splint.org
** To report a bug: splint-bug@splint.org
** For more information: http://www.splint.org
*/
/*
** library.c
**
** routines for loading and creating library files
**
** this is a brute-force implementation, a more efficient
** representation should be designed.
**
*/

# include <float.h>

# include "splintMacros.nf"
# include "basic.h"
# include "osd.h"

# include "libversion.h"
# include "library.h"

static void printDot (void)
{
  displayScanContinue (cstring_makeLiteralTemp ("."));
}

static bool loadLCDFile (FILE * p_f, cstring p_name);

void
dumpState (cstring cfname)
{
  FILE *f;
  cstring fname = fileLib_addExtension (cfname, cstring_makeLiteralTemp (DUMP_SUFFIX));
  
  f = fileTable_openWriteFile (context_fileTable (), fname);

  displayScanOpen (message ("Dumping to %s ", fname)); 
  
  if (f == NULL)
    {
      lldiagmsg (message ("Cannot open dump file for writing: %s", fname));
    }
  else
    {
      /*
      ** sequence is convulted --- must call usymtab_prepareDump before
      **    dumping ctype table to convert type uid's
      */

      printDot ();

# if 0
      DPRINTF (("Before prepare dump:"));
      ctype_printTable ();
      DPRINTF (("Preparing dump..."));
# endif

      usymtab_prepareDump ();

      /*
      ** Be careful, these lines must match loadLCDFile checking.
      */

      fprintf (f, "%s %s\n", LIBRARY_MARKER, cstring_toCharsSafe (fname));
      fprintf (f, ";;Splint %f\n", LIBVERSION);
      fprintf (f, ";;lib:%d\n", (int) context_getLibrary ());
      fprintf (f, ";;ctTable\n");
      
      DPRINTF (("Dumping types..."));
      printDot ();
      ctype_dumpTable (f);
      printDot ();
      
      DPRINTF (("Dumping type sets..."));
      fprintf (f, ";;tistable\n");
      typeIdSet_dumpTable (f);
      printDot ();
      
      DPRINTF (("Dumping usymtab..."));
      fprintf (f, ";;symTable\n");
      usymtab_dump (f);
      printDot ();

      DPRINTF (("Dumping modules..."));
      fprintf (f, ";; Modules access\n");
      context_dumpModuleAccess (f);
      fprintf (f, ";;End\n");
      check (fileTable_closeFile (context_fileTable (), f));
    }

  displayScanClose ();
  cstring_free (fname);
}

bool
loadStandardState (void)
{
  cstring fpath;
  FILE *stdlib;
  bool result;
  cstring libname = fileLib_addExtension (context_selectedLibrary (), 
					  cstring_makeLiteralTemp (DUMP_SUFFIX));
  
  if (osd_getPath (context_getLarchPath (), libname, &fpath) != OSD_FILEFOUND)
    {
      lldiagmsg (message ("Cannot find standard library: %s", libname));
      lldiagmsg (cstring_makeLiteral ("     Check LARCH_PATH environment variable."));
      result = FALSE;
    }
  else
    {
      stdlib = fileTable_openReadFile (context_fileTable (), fpath);

      if (stdlib == NULL)
	{
	  lldiagmsg (message ("Cannot read standard library: %s",
			      fpath));
	  lldiagmsg (cstring_makeLiteral ("     Check LARCH_PATH environment variable."));

	  result = FALSE;
	}
      else
	{
	  if (context_getFlag (FLG_WHICHLIB))
	    {
	      char *t = mstring_create (MAX_NAME_LENGTH);
	      char *ot = t;

	      if ((t = reader_readLine (stdlib, t, MAX_NAME_LENGTH)) == NULL)
		{
		  llfatalerror (cstring_makeLiteral ("Standard library format invalid"));
		}

	      if ((t = reader_readLine (stdlib, t, MAX_NAME_LENGTH)) != NULL)
		{
		  if (*t == ';' && *(t + 1) == ';') 
		    {
		      t += 2;
		    }
		}

	      if (t == NULL)
		{
		  lldiagmsg (message ("Standard library: %s <cannot read creation information>", 
				      fpath));
		}
	      else
		{
		  char *tt;

		  tt = strrchr (t, '\n');
		  if (tt != NULL)
		    *tt = '\0';

		  lldiagmsg (message ("Standard library: %s", fpath));
		  /* evans 2004-01-13: removed this (it is the libversion which is confusing) */
		  /*   lldiagmsg (message ("   (created using %s)", cstring_fromChars (t)));		    */
		}

	      sfree (ot);
	      
	      check (fileTable_closeFile (context_fileTable (), stdlib));
	      stdlib = fileTable_openReadFile (context_fileTable (), fpath);
	    }

	  llassert (stdlib != NULL);

	  fileloc_reallyFree (g_currentloc);
	  g_currentloc = fileloc_createLib (libname, TRUE);

	  DPRINTF (("Loading: %s", fpath));

	  displayScanOpen (message ("loading standard library %s ", fpath));
	  result = loadLCDFile (stdlib, fpath);
	  displayScanClose ();

	  check (fileTable_closeFile (context_fileTable (), stdlib));
	}
    }

  cstring_free (libname);
  return result;
}

/*@constant int BUFLEN;@*/
# define BUFLEN 128

static bool
loadLCDFile (FILE *f, cstring name)
{
  char buf[BUFLEN];
  
  if (reader_readLine (f, buf, BUFLEN) == NULL
      || !mstring_equalPrefix (buf, LIBRARY_MARKER))
    {
      loadllmsg (message ("Load library %s is not in Splint library format.  Attempting "
			  "to continue without library.", name));
      return FALSE;
    }
  
  if (reader_readLine (f, buf, BUFLEN) != NULL)
    {
      if (!mstring_equalPrefix (buf, ";;"))
	{
	  loadllmsg (message ("Load library %s is not in Splint library format.  Attempting "
			      "to continue without library.", name));
	  return FALSE;
	}
      else if (mstring_equalPrefix (buf, ";;ctTable"))
	{
	  loadllmsg (message ("Load library %s is in obsolete Splint library format.  Attempting "
			      "to continue anyway, but results may be incorrect.  Rebuild "
			      "the library with this version of splint.", 
			      name));
	}
      else 
	{
	  float version = 0.0;

	  if (sscanf (buf, ";;Splint %f", &version) != 1
	      && (sscanf (buf, ";;LCLint %f", &version) != 1))
	    {
	      loadllmsg (message ("Load library %s is not in Splint library format (missing version "
				  "number).  Attempting to continue without library.", name));
	      return FALSE;
	    }
	  else
	    {
              /*
              ** Check version.  Should be >= LIBVERSION
              */

	      if ((LIBVERSION - version) >= FLT_EPSILON)
		{
		  cstring vname;
		  char *nl = strchr (buf, '\n');

		  *nl = '\0';

		  vname = cstring_fromChars (buf + 9);

		  loadllmsg (message ("Load library %s is in obsolete Splint library "
				      "format (version %f (%s), expecting version %f).  Attempting "
				      "to continue anyway, but results may be incorrect.  Rebuild "
				      "the library with this version of splint.", 
				      name, 
				      version,
				      vname, 
				      LIBVERSION));
		}
	      else
		{
		  if (reader_readLine (f, buf, BUFLEN) == NULL)
		    {
		      loadllmsg (message ("Load library %s is not in Splint library "
					  "format (missing library code). Attempting "
					  "to continue without library.", name));
		      return FALSE;
		    }
		  else 
		    {
		      int lib;
		      
		      if (sscanf (buf, ";;lib:%d", &lib) != 1)
			{
			  loadllmsg (message ("Load library %s is not in Splint library "
					      "format (missing library code). Attempting "
					      "to continue without library.", name));
			  return FALSE;
			}
		      else
			{
			  flagcode code = (flagcode) lib;

			  if (flagcode_isLibraryFlag (code))
			    {
			      if (context_doMerge ()) 
				{
				  context_setLibrary (code);
				}
			    }
			  else
			    {
			      loadllmsg (message ("Load library %s has invalid library code (%s).  "
						  "Attempting to continue without library.",
						  name,
						  flagcode_unparse (code)));
			      
			      return FALSE;
			    }
			}
		    }
		}
	    }
	}
    }
  else
    {
      loadllmsg (message ("Load library %s is not in Splint library format (missing lines).  "
			  "Attempting to continue without library.", name));
      return FALSE;
    }
  
  ctype_loadTable (f);
  printDot ();
  
  typeIdSet_loadTable (f);
  printDot ();
  
  usymtab_load (f);
  printDot ();
  
  context_loadModuleAccess (f);
  printDot ();
  
  return TRUE;
}

/*
** load state from file created by dumpState
*/

void
loadState (cstring cfname)
{
  FILE *f;
  cstring fname = fileLib_addExtension (cfname, cstring_makeLiteralTemp (DUMP_SUFFIX));

  f = fileTable_openReadFile (context_fileTable (), fname);

  if (f == NULL)
    {
      displayScanClose ();
      llfatalerror (message ("Cannot open dump file for loading: %s", 
			     fname));
    }
  else
    {
      fileloc_reallyFree (g_currentloc);
      g_currentloc = fileloc_createLib (cfname, FALSE);

      if (!loadLCDFile (f, cfname)) 
	{
	  if (!loadStandardState ()) 
	    {
	      ctype_initTable ();
	    }
	}
      
      check (fileTable_closeFile (context_fileTable (), f));
    }

  /* usymtab_printAll (); */
  cstring_free (fname);
}

