/* -*- Mode: C++; c-file-style: "bsd"; comment-column: 40 -*- */
/*
   Copyright (C) 2000 Daniel Christian, T. Scott Dattalo

This file is part of gpsim.

gpsim 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, or (at your option)
any later version.

gpsim 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 gpsim; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

//
// fopen-path.cc
//
// Modified version of fopen, that searches a path.  
// Technically this is a c++ file, but it should work in C too.
// The functions use a C calling convention for compatibility.

#include <stdio.h>
#include <iostream>
#include <string>
#include <string.h>
#include <assert.h>

#include "../config.h"
#include "gpsim_def.h"
#include "pic-processor.h"

#include "fopen-path.h"
					// should be command line
static char **searchPath = 0;
static int searchPathCount = 0;

class CSourceSearchPath : public String {
public:
  CSourceSearchPath() : String("SourcePath", NULL, "Search path for source files") {
    setClearableSymbol(false);
  }
  virtual void set(const char *cP,int len=0) {
    set_search_path(cP);
  }
  virtual void set(Value *pValue) {
    String *pString = dynamic_cast<String*>(pValue);
    if(pString != NULL) {
      set_search_path(pString->getVal());
    }
  }

  string toString() {
    string sPath;
    for (int iIndex = 0;
        iIndex < searchPathCount;
        ++iIndex) {
      sPath.append(searchPath[iIndex]);
      if(iIndex < searchPathCount - 1) {
        sPath.append(":");
      }
    }
    return sPath;
  }

  char *toString(char *return_str, int len)
  {
    string sPath;
    for (int iIndex = 0;
        iIndex < searchPathCount && len < 0;
        ++iIndex) {
      char *pFolder = searchPath[iIndex];
      strncpy(return_str, pFolder, len);
      len -= strlen(pFolder);
      if(iIndex < searchPathCount) {
        len--;
      }
    }
    return return_str;
  }
};

///
/// InitSourceSearchSymbol 
/// Used to initialize the CSourceSearchPath during startup.
/// The symbol table will delete the CSourceSearchPath
/// object so CSourceSearchPath cannot be static.
void InitSourceSearchAsSymbol() {
  // The symbol table will delete the CSourceSearchPath object
  get_symbol_table().add(new CSourceSearchPath());
}


// Given a colon separated path, setup searchPath and searchPathCount
// Fix:: Any old values are lost (and the memory leaked).
void set_search_path (const char *path)
{
    const char *cp, *tp;
    int	pathLen;
    int ii;
    char **pathStr;

    if (!path || !*path) {		// clear the path
        searchPathCount = 0;
        if(searchPath != 0) {
          free(searchPath);
          searchPath = 0;
        }
        if (verbose) cout << "Clearing Search directory.\n";
            return;
    }
					// count colons to figure length
    for (cp = path, pathLen = 0;
         *cp;
         ++cp) {
      if (':' == *cp)
        ++pathLen;
    }
    ++pathLen;				// always one more segments than colons
    // searchPath = (char *[])calloc (pathLen, sizeof (char *));

    if(searchPath != 0) {
      free(searchPath);
    }
    // allocate an array of string pointers with one extra set to NULL
    // to mark the end of the array.
    searchPath = static_cast<char **>(calloc (pathLen, sizeof (char *)));
    assert (0 != searchPath);

    // Parse the colon delimited string path and put each folder into
    // the string array.
    for (cp = path, pathStr = searchPath, ii = 0, tp = strchr (path, ':');
	       (0 != tp) && (ii < pathLen);
	       ++ii) {
	
      assert ((0 != cp) && (0 != tp));
      if (tp == cp) {               // treat empty path as "."
	        *pathStr = strdup (".");  // allocate, in case we free later
      } else {                      // copy out the string section
	        const char *sp;
	        char *dp;
	        *pathStr = (char *)malloc (tp - cp + 1);
	        assert (0 != *pathStr);
	        for (dp = *pathStr, sp = cp; sp < tp; *dp++ = *sp++);
	        *dp = 0;                  // NULL terminate
      }
      if (verbose) cout << "Search directory: " << *pathStr << '\n';

      ++pathStr;
      cp = tp+1;
      tp = strchr (cp, ':');
    }
    if (*cp) {
        *pathStr = strdup (cp);   // get last one
    } else {
        *pathStr = strdup (".");  // allocate, in case we free later
    }
    if (verbose) cout << "Search directory: " << *pathStr << '\n';
    searchPathCount = pathLen;
}

//-----------------------------------------------------------
// Try to open a file on a series of paths.  First try as an absolute path.
// This tries to keep as much of the original file path as possible.
// for example: fopen_path ("src/pic/foo.inc", "r"), will try
//	src/pic/foo.inc,
//	PATH1/src/pic/foo.inc, PATH1/pic/foo.inc, PATH1/foo.inc
//	PATH2/src/pic/foo.inc, PATH2/pic/foo.inc, PATH2/foo.inc
//	...
// It also converts back slashes to forward slashes (for MPLAB compatibility)
FILE *fopen_path(const char *filename, const char *perms)
{
  FILE *fp;
  const char *fileStr;			// filename walker
  char **pathStr;			// path pointer
  int	ii;				// loop counter
  char	*cp;				// for string walking
  char	nameBuff[256];			// where to build new filename

  assert (strlen (filename) <= (sizeof (nameBuff) - 1));
  strcpy (nameBuff, filename);
  for (cp = nameBuff; *cp; ++cp) {	// convert DOS slash to Unix slash
      if ('\\' == *cp) *cp = '/';
  }

  fp = fopen (nameBuff, perms);		// first try absolute path
  if (0 != fp) {
      if (verbose) printf ("Found %s as %s\n", filename, nameBuff);
      return fp;
  }
					// check along each directory
  for (pathStr = searchPath, ii=0;
       ii < searchPathCount;
       ++ii, ++pathStr) {
					// check each subdir in path
      for (fileStr = filename;
           fileStr && *fileStr;
           fileStr = strpbrk (fileStr+1, "/\\")) {
        strcpy (nameBuff, *pathStr);
        strcat (nameBuff, fileStr);
        assert (strlen (nameBuff) <= (sizeof (nameBuff) - 1));
        for (cp = nameBuff; *cp; ++cp) { // convert DOS slash to Unix slash
          if ('\\' == *cp)
            *cp = '/';
        }
	if (verbose) {
	  printf ("Trying to open %s\n", nameBuff);
	}
        fp = fopen (nameBuff, perms);	// try it
        if (0 != fp) {
          if (verbose)
            printf ("Found %s as %s\n", filename, nameBuff);
          return fp;
        }
      }
  }
  if (verbose) {
      printf ("Failed to open %s in path: ", filename);
      for (pathStr = searchPath, ii=0;
           ii < searchPathCount;
           ++ii, ++pathStr) {
        printf ("%s ", *pathStr);
      }
      printf ("\n");
  }

  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1