/*
 * Copyright (c) 2002, The EROS Group, LLC and Johns Hopkins
 * University. All rights reserved.
 * 
 * This software was developed to support the EROS secure operating
 * system project (http://www.eros-os.org). The latest version of
 * the OpenCM software can be found at http://www.opencm.org.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 * 
 * 3. Neither the name of the The EROS Group, LLC nor the name of
 *    Johns Hopkins University, nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <opencm.h>
#include <repos/Repository.h>
#include "opencmclient.h"

static Filter global_filter_list[] = {
  { TRUE, "*.o" },		/* no object files */
  { TRUE, "*.a" },		/* no archive files */
  { TRUE, "core" },		/* no unix core files */
  { TRUE, "core.*" },		/* REALLY no unix core files */
  { TRUE, "gmon.out" },		/* prof output */
  { TRUE, "bb.out" },		/* gprof output */
  { TRUE, "*.m" },		/* no dependency files */
  { TRUE, ".*.m" },		/* or hidden dependency files */
  { TRUE, "*~" },		/* no emacs versions */
  { TRUE, ".*~" },		/* or hidden emacs versions */
  { TRUE, ".#*" },		/* no emacs editor buffer turds */
  { TRUE, "#*" },		/* ... or other emacs editor buffer turds */
  { TRUE, "CVS" },		/* Ignore CVS subdirs */
  { TRUE, ".cvsignore" },	/* Ignore .cvsignore files */
  { TRUE, ".*.swp" },          /* no vim swap files */
  { TRUE, CM_CONFIG_DIR },	/* no OpenCM turds either */
  
  { TRUE, 0 },	/* END MARKER */
};

FilterSet *GlobalFilters;

void
filterset_append(FilterSet *fs, Filter *f)
{
  fs->filters = (Filter **) GC_REALLOC(fs->filters,
				     (fs->size + 1) * sizeof(Filter *)); 
  fs->filters[fs->size] = f;
  fs->size++;
}

void
filterset_init(void)
{
  Filter *list = global_filter_list;
  
  GlobalFilters = (FilterSet *) GC_MALLOC(sizeof(FilterSet)); 
  GlobalFilters->filters = 0;
  GlobalFilters->size = 0;
  
  while (list->pattern) {
    filterset_append(GlobalFilters, list);
    list++;
  }
}

OC_bool
filterset_ShouldExclude(FilterSet *fs, const char *nm, OC_bool exclude)
{
  unsigned u;
  
  const char *basenm = strrchr(nm, '/');
  basenm++;

  for (u = 0; u < fs->size; u++) {
    if (glob_match(basenm, fs->filters[u]->pattern, GM_EXACT))
      exclude = fs->filters[u]->exclude;
  }

  return exclude;
}

FilterSet *
filterset_LoadFrom(const char *dir)
{
  const char *filtfile;
  FILE *f = NULL;
  
  FilterSet *fs = (FilterSet *) GC_MALLOC(sizeof(FilterSet)); 
  fs->filters = 0;
  fs->size = 0;

  filtfile = path_join(dir, OPENCM_LOCAL_FILTER_FILE);
  
  if (path_exists(filtfile) == FALSE)
    return fs;
  
  f = xfopen(filtfile, 'r', 't');

  TRY {
    unsigned len = path_file_length(filtfile); /* upper bound if text */
  
    /* buffer is used to hold file data, so use _ATOMIC */
    char *buffer = (char *) GC_MALLOC_ATOMIC(len);
    char *bufend;
    char *bufp;
    Filter *filt;
    
    /* Length may get truncated if this is a WIN32 text file */

    /* What happens if someone appends to the file between now and when we
       grabbed the length above? I suppose even if we were reading until EOF
       something like that could still happen. Anyway, something to think
       about. - JL (10/8/2002) */
    len = fread(buffer, 1, len, f);

    bufend = buffer + len;

    for (bufp = buffer; bufp != bufend; bufp++) {
      if (*bufp == '#') {
	/* Skip to end of line */
	while (*bufp != '\n' && bufp != bufend)
	  bufp++;
	continue;
      }
      else if ((bufend - bufp) > 7 &&
	       (strncmp(bufp, "include", 7) || strncmp(bufp, "exclude", 7))) {
	/* Looking at something that is the start of a regexp. Increment
	   the count and advance to the end of the line. */

	char *start;
	int len;
	OC_bool exclude = (*bufp == 'e');
      
	bufp += 7;
      
	while (isspace(*bufp) && (*bufp != '\n') && bufp != bufend)
	  bufp++;
	       
	start = bufp;

	while (*bufp != '\n' && bufp != bufend)
	  bufp++;

	len = bufp - start;

	filt = (Filter *) GC_MALLOC(sizeof(Filter)); 
	filt->exclude = exclude;
	filt->pattern = xstrndup(start, len);

	filterset_append(fs, filt);
      }

      /* May have eaten all the way up to the end of the buffer, in
	 which case we don't want to loop anymore */
      if (bufp == bufend)
	break;
    }

    xfclose(f);
  }
  DEFAULT(ex) {
    xfclose(f);
    RETHROW(ex);
  }
  END_CATCH;
  
  return fs;
}
