/*
** 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
*/
/*
** fileLib.c
*/

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

bool
fileLib_isCExtension (cstring ext)
{
  return (cstring_equalLit (ext, ".c") 
	  || cstring_equalLit (ext, ".C") 
	  || cstring_equalLit (ext, ".h")
	  || cstring_equalLit (ext, ".lh")
	  || cstring_equalLit (ext, ".xh")
	  || cstring_equalLit (ext, ".H")
	  || cstring_equalLit (ext, ".y")
	  || cstring_equalLit (ext, ".l"));
}

bool
fileLib_isLCLFile (cstring s)
{
  return cstring_equal (fileLib_getExtension (s), LCL_EXTENSION);
}

/*@only@*/ cstring fileLib_withoutExtension (/*@temp@*/ cstring s, cstring suffix)
{
  /*@access cstring@*/
  char *t;
  char *s2;

  if (cstring_isUndefined (s)) {
    return cstring_undefined;
  }

  t = strrchr (s, '.');
  if (t == (char *) 0 || !mstring_equal (t, suffix))
    {
      return mstring_copy (s);
    }

  /*@-mods@*/ 
  *t = '\0';
  s2 = mstring_copy (s);
  *t = '.';
  /*@=mods@*/  /* Modification is undone. */
  return s2;
  /*@noaccess cstring@*/
}

/*@only@*/ cstring fileLib_removePath (cstring s)
{
  /*@access cstring@*/
  if (cstring_isUndefined (s)) {
    return cstring_undefined;
  }

  return mstring_copy (osd_pathBase (s));
  /*@noaccess cstring@*/
}

/*@only@*/ cstring
fileLib_removePathFree (/*@only@*/ cstring s)
{
  /*@access cstring@*/
  char *t;

  if (cstring_isUndefined (s)) {
    return cstring_undefined;
  }

  t = osd_pathBase (s);

  if (t == s) 
    {
      return s;
    }
  else
    {
      char *res = mstring_copy (t);
      mstring_free (s);
      return res;
    }
  /*@noaccess cstring@*/
}

/*@only@*/ cstring
fileLib_removeAnyExtension (cstring s)
{
  /*@access cstring@*/
  char *ret;
  char *t;


  if (cstring_isUndefined (s)) {
    return cstring_undefined;
  } 

  t = strrchr (s, '.');

  if (t == (char *) 0)
    {
      return mstring_copy (s);
    }

  /*@-mods@*/
  *t = '\0';
  ret = mstring_copy (s);
  *t = '.';
  /*@=mods@*/ /* modification is undone */

  return ret;
  /*@noaccess cstring@*/
}

/*@only@*/ cstring
fileLib_addExtension (/*@temp@*/ cstring s, cstring suffix)
{
  /*@access cstring@*/
  llassert (cstring_isDefined (s));

  if (strrchr (s, '.') == (char *) 0)
    {
      /* <<< was mstring_concatFree1 --- bug detected by splint >>> */
      return (cstring_concat (s, suffix));
    }
  else
    {
      return cstring_copy (s);
    }
}

/*@observer@*/ cstring fileLib_getExtension (/*@returned@*/ cstring s)
{
  llassert (cstring_isDefined (s));

  /*@access cstring@*/
  return (strrchr(s, '.'));
  /*@noaccess cstring@*/
}

bool fileLib_isHeader (cstring fname)
{
  cstring ext = fileLib_getExtension (fname);
  
  return (cstring_equalLit (ext, ".h")
	  || cstring_equalLit (ext, ".H")
	  || cstring_equal (ext, LH_EXTENSION));
}

bool
fileLib_isSpecialFile (cstring fname)
{
  cstring ext = fileLib_getExtension (fname);
  
  return (cstring_equalLit (ext, ".y") 
	  || cstring_equalLit (ext, ".l")
	  || cstring_equalLit (fname, "lex.yy.c"));
}

cstring fileLib_cleanName (cstring s)
{
  if (cstring_equalPrefixLit (s, "./")) 
    {
      cstring res = cstring_copySegment (s, 2, cstring_length (s) - 1);
      cstring_free (s);
      return res;
    }

  return s;
}

/*@constant int NUMSTDLIBS; @*/
# define NUMSTDLIBS 25

static /*@observer@*/ const char* stdlibs[NUMSTDLIBS] =
{
  "assert", 
  "complex",
  "ctype",
  "errno",
  "fenv",
  "float",
  "inttypes",
  "iso646",
  "limits",
  "locale",
  "math", 
  "setjmp",
  "signal",
  "stdarg",
  "stdbool",
  "stddef",
  "stdint",
  "stdio",
  "stdlib",
  "string",
  "strings", /* some systems use this...they shouldn't */
  "tgmath",
  "time",
  "wchar",
  "wctype"
} ;

/*@constant int NUMPOSIXLIBS; @*/
# define NUMPOSIXLIBS 21

static /*@observer@*/ const char* posixlibs[NUMPOSIXLIBS] = 
{
  "dirent",
  "fcntl",
  "grp",
  "pwd",
  "regex",
  "sys/stat",
  "sys/times",
  "sys/types",
  "netdb",
  "netinet/in",
  "poll",
  "sys/resource",
  "sys/socket",
  "sys/syslog",
  "sys/uio",
  "sys/utsname",
  "sys/wait",
  "termios",
  "ucontext", /* obsolete */
  "unistd",
  "utime"
} ;

bool
fileLib_isSkipHeader (cstring sname)
{
  int i;
  bool posixlib = FALSE;
  char *libname;
  char *matchname;
  cstring xname;

  llassert (cstring_isDefined (sname));
  xname = fileLib_withoutExtension (sname, cstring_makeLiteralTemp (".h"));

  DPRINTF (("Include? %s", sname));

  /*@access cstring@*/
  llassert (cstring_isDefined (xname));
  osd_pathMakePosix (xname);
  libname = strrchr (xname, '/');
  DPRINTF (("libname: %s", libname));
  matchname = libname;

  if (libname == NULL) 
    {
      libname = xname;
    }
  else
    {
      libname++;
      /*@-branchstate@*/
    }
  /*@=branchstate@*/

  if (mstring_equal (libname, "varargs"))
    {
      fileloc tmp = fileloc_makePreprocPrevious (g_currentloc);
      
      voptgenerror 
	(FLG_USEVARARGS,
	 message ("Include file <%s.h> is inconsistent with "
		  "ANSI library (should use <stdarg.h>)",
		  cstring_fromChars (libname)),
	 tmp);
      
      fileloc_free (tmp);
      cstring_free (xname);
      return TRUE;
    }

  if (context_getFlag (FLG_SKIPSTDHEADERS)
      && context_usingStdLibrary ())
    {
      for (i = 0; i < NUMSTDLIBS; i++)
	{
	  if (mstring_equal (libname, stdlibs[i]))
	    {
	      sfree (xname);
	      return TRUE;
	    }
	}
    }

  for (i = 0; i < NUMPOSIXLIBS; i++)
    {
      if (osd_pathHasDir(posixlibs[i]))
	{
	  char *ptr;
	  
	  DPRINTF (("xname: %s, posix: %s", xname, posixlibs[i]));
	  if ((ptr = strstr (xname, posixlibs[i])) != NULL) 
	    {
	      if (ptr[strlen (posixlibs[i])] == '\0')
		{
		  posixlib = TRUE;
		  matchname = ptr;
		  break;
		}
	      else
		{
		  ; /* no match */
		}
	    }
	}
      else
	{
	  if (mstring_equal (libname, posixlibs[i]))
	    {
	      posixlib = TRUE;
	      matchname = libname;
	      break;
	    }
	  /*@-branchstate@*/ 
	}
    } /*@=branchstate@*/
  
  if (posixlib)
    {
      if (context_usingPosixLibrary ())
	{
	  if (context_getFlag (FLG_SKIPPOSIXHEADERS))
	    {
	      cstring_free (xname);
	      /*@-nullstate@*/ 
	      return TRUE; 
	      /*@=nullstate@*/

	      /* evans 2002-03-02: 
		   the returned reference is possibly null,
  		   but this should not change the null state of the parameter
	      */
	    }
	}
      else
	{	
	  fileloc tmp = fileloc_makePreprocPrevious (g_currentloc);	      
	  
	  voptgenerror 
	    (FLG_WARNPOSIX,
	     message ("Include file <%s.h> matches the name of a "
		      "POSIX library, but the POSIX library is "
		      "not being used.  Consider using +posixlib "
		      "or +posixstrictlib to select the POSIX "
		      "library, or -warnposix "
		      "to suppress this message.",
		      cstring_fromChars (matchname)),
	     tmp);
	  
	  fileloc_free (tmp);
	}
    }

  cstring_free (xname);
  /*@noaccess cstring@*/
  /*@-nullstate@*/ /* same problem as above */
  return FALSE;
  /*@=nullstate@*/
}

