#!/usr/bin/ruby -w
# -*- ruby -*-

require './regexp'
require './log'


# Represents .p4ignore files, which are modeled on .cvsignore files.

class IgnoredPatterns < Hash
  include Log

  def initialize(ignorename)
    @ignorename = ignorename
    @dirsread = Array.new
  end

  def read(dir)
    # from the CVS default settings -- ignoring overrides

    log "reading ignored patterns for " + dir

    return if @dirsread.include?(dir)
    @dirsread.push(dir)

    pats = %w{
                  CVS
                  *~
                  .p4ignore
                  .p4config
                  *.o
                  *$
                  *.BAK
                  *.Z
                  *.a
                  *.bak
                  *.elc
                  *.exe
                  *.ln
                  *.obj
                  *.olb
                  *.old
                  *.orig
                  *.rej
                  *.so
                  .
                  ..
                  .del-*
                  .make.state
                  .nse_depinfo
                  CVS.adm
                  RCS
                  RCSLOG
                  SCCS
                  TAGS
                  _$*
                  core
                  cvslog.*
                  tags
              }
    
    # can't put these guys in the qw() list:
    ['.#*', '#*', ',*'].each { |p| pats.push(p) }

    # read ~/<ignore>
    homedir = ENV["HOME"]       # unix
    unless homedir              # windows
      homedir  = ENV["HOMEDRIVE"]
      homepath = ENV["HOMEPATH"]
      if homepath then
        if homedir then
          homedir += homepath
        else
          homedir = homepath
        end
      end
    end
    
    global = read_ignore_file(homedir)
    pats.push(*global) unless global.length == 0

    # read <ignore> in the current directory
    local = read_ignore_file(dir)
    pats.push(*local) unless local.length == 0

    # prepend the current directory to the patterns, contending with the fact
    # that the directory might actually be a valid regular expression.

    # wildcard if the pattern is a directory
    pats = pats.collect do |p|
      p += "/*" if File.directory?(dir + "/" + p)
      p
    end

    qdir = Regexp.quote(dir)
    pats = pats.collect do |p| 
      p = Regexp.unixre_to_string(p)
      qdir + "/" + p
    end

    # make a regular expression for each one, to be the entire string (^...$)
    self[dir] = Array.new
    pats.each do |p| 
      re = Regexp.new("^" + p + "$")
      # log "IgnoredPatterns: storing re " + re.source + " for dir " + dir
      self[dir].push(re)
    end
  end

  def read_ignore_file(dir)
    pats = Array.new

    if dir then
      cifile = dir + "/" + @ignorename
      if File.exists?(cifile)
        IO.foreach(cifile) do |line|
          line.chomp!
          line.gsub!(/\+/, '\\+')
          pats.push(*line.split)
        end
      else
        log "no ignore file in " + dir
      end
    end
    pats

  end

  # Returns if the file is ignored. Checks the name as both "./name" and "name".

  def is_ignored?(name)
    log "is_ignored?(" + name + ")"
    if name.index("./") == 0
      withpref, nopref = name, name.sub!("./", "")
    else
      withpref, nopref = "./" + name, name
    end
    
    [ withpref, nopref ].each do |name|
      dir = name
      log "dirs = " + keys.join(", ")
      while dir = File.dirname(dir)
        if include?(dir)
          regexps = self[dir]
          regexps.each do |re|
            # log "matching " + name + " against " + re.source
            # stop as soon as we find out it is ignored
            return true if re.match(name)
          end
        else
          log "dir " + dir + " is not included"
        end
        break if dir == "."     # else we'll cycle continuously
      end
    end
    
    return false              # it's not ignored
  end

end


syntax highlighted by Code2HTML, v. 0.9.1