Skip to content
This repository has been archived by the owner on Mar 29, 2023. It is now read-only.

Commit

Permalink
folddir.ml: trailing slash in .gitignore only matches directory
Browse files Browse the repository at this point in the history
This patch contains lots of incidental indentation changes due to
added wrapper functions. You might like to view it with git diff -b to
filter out the indentation noise.

Developed and tested on Mac OS X 10.4.11 with Ocaml 3.11.0.

Signed-off-by: Chris Johnsen <chris_johnsen@pobox.com>
  • Loading branch information
ChrisJohnsen committed Jul 20, 2009
1 parent fa57411 commit 8dde54b
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 62 deletions.
4 changes: 0 additions & 4 deletions README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,6 @@ When you want to sync the laptop, just:
Known Bugs
==========

* .gitignore patterns ending in "/" should match directories, but not files, per "man gitignore".
We fail to match them at all! Workaround: be sure that none of your directory patterns will
accidentally match a file, and then just remove the trailing slashes.

* ometastore gets confused trying to do chown and utime on symlinks. It spits a harmless error
out, but this should be cleaned up.

Expand Down
133 changes: 75 additions & 58 deletions folddir.ml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sig
type t
val init : string -> t
val update : t -> base:string -> path:string -> t
val is_ignored : ?debug:bool -> t -> string -> bool
val is_ignored : ?debug:bool -> t -> string -> bool -> bool
end

let join a b = if a <> "" && b <> "" then a ^ "/" ^ b else a ^ b
Expand Down Expand Up @@ -44,17 +44,20 @@ struct
List.iter
(function
"." | ".." -> ()
| n when M.is_ignored ~debug ign_info n -> ()
| n ->
let n = join path n in
let stat = lstat (join base n) in
match f !acc n stat with
| Continue x ->
acc := x;
if stat.st_kind = S_DIR then
acc := fold_directory ~debug ~sorted f ~ign_info
!acc base n
| Prune x -> acc := x)
let path_without_base = join path n in
let stat = lstat (join base path_without_base) in
let n_is_dir = stat.st_kind = S_DIR in
if M.is_ignored ~debug ign_info n n_is_dir then
()
else
match f !acc path_without_base stat with
| Continue x ->
acc := x;
if n_is_dir then
acc := fold_directory ~debug ~sorted f
~ign_info !acc base path_without_base
| Prune x -> acc := x)
(let l = readd d in if sorted then List.sort compare l else l));
!acc
with Unix.Unix_error _ -> !acc
Expand All @@ -65,7 +68,7 @@ struct
type t = unit
let init _ = ()
let update () ~base ~path = ()
let is_ignored ?debug () _ = false
let is_ignored ?debug () _ _ = false
end

module Gitignore : IGNORE =
Expand All @@ -88,17 +91,22 @@ struct
| Endswith of string | Endswith_local of string
| Startswith_local of string
| Nowildcard of string * int
type glob = glob_type * patt
type file_or_dir = File_Or_Dir | Dir_Only
type glob = glob_type * (file_or_dir * patt)
type t = (string * glob list) list

external fnmatch : bool -> string -> string -> bool = "perform_fnmatch" "noalloc"

let string_of_patt = function
Simple s | Noslash s | Complex s | Nowildcard (s, _) -> s
let string_of_patt =
let string_of_patt_fod = function
Simple s | Noslash s | Complex s | Nowildcard (s, _) -> s
| Simple_local s -> "/" ^ s
| Endswith s -> "*" ^ s
| Endswith_local s -> "/*" ^ s
| Startswith_local s -> "/" ^ s ^ "*"
in function
(File_Or_Dir, p) -> string_of_patt_fod p
| (Dir_Only, p) -> (string_of_patt_fod p) ^ "/"

let has_wildcard s =
let rec loop s i max =
Expand All @@ -113,35 +121,40 @@ struct
let pref1 s = String.sub s 0 (String.length s - 1)

let patt_of_string s =
try
match String.rindex s '/' with
0 ->
let s = suff1 s in
if not (has_wildcard s) then Simple_local s
let patt_of_string_fod s =
try
match String.rindex s '/' with
0 ->
let s = suff1 s in
if not (has_wildcard s) then Simple_local s
else
let suff = suff1 s in
if s.[0] = '*' && not (has_wildcard suff) then
Endswith_local suff
else
let pref = pref1 s in
if s.[String.length s - 1] = '*' && not (has_wildcard pref) then
Startswith_local pref
else
Complex s
| _ ->
if not (has_wildcard s) then
let l = String.length s in
Nowildcard (s, if s.[0] = '/' then l - 1 else l)
else
let suff = suff1 s in
if s.[0] = '*' && not (has_wildcard suff) then
Endswith_local suff
else
let pref = pref1 s in
if s.[String.length s - 1] = '*' && not (has_wildcard pref) then
Startswith_local pref
else
Complex s
| _ ->
if not (has_wildcard s) then
let l = String.length s in
Nowildcard (s, if s.[0] = '/' then l - 1 else l)
Complex s
with Not_found ->
if s = "" then Simple s else
let suff = suff1 s in
if s.[0] = '*' && not (has_wildcard suff) then
Endswith suff
else if has_wildcard s then
Noslash s
else
Complex s
with Not_found ->
let suff = suff1 s in
if s.[0] = '*' && not (has_wildcard suff) then
Endswith suff
else if has_wildcard s then
Noslash s
else
Simple s
Simple s
in match s.[String.length s - 1] with
'/' -> (Dir_Only, patt_of_string_fod (pref1 s))
| _ -> (File_Or_Dir, patt_of_string_fod s)

let glob_of_string s = match s.[0] with
'!' -> (Accept, (patt_of_string (suff1 s)))
Expand Down Expand Up @@ -174,9 +187,9 @@ struct
let update t ~base ~path =
let rec remove_local = function
[] -> []
| ((_, (Simple _ | Noslash _ | Complex _ | Endswith _ | Nowildcard _)) as x)::tl ->
| ((_, (_, (Simple _ | Noslash _ | Complex _ | Endswith _ | Nowildcard _))) as x)::tl ->
x :: remove_local tl
| (_, (Simple_local _ | Endswith_local _ | Startswith_local _))::tl ->
| (_, (_, (Simple_local _ | Endswith_local _ | Startswith_local _)))::tl ->
remove_local tl in
let t = match t with
(f, l)::tl -> (f, remove_local l)::tl
Expand Down Expand Up @@ -210,28 +223,32 @@ struct
if l2 < l1 then false else
strneq l1 pref 0 fname 0

let glob_matches local patt path = match patt with
Simple s -> s = basename path
| Simple_local s -> if local then s = basename path else false
| Endswith s -> check_ending s path
| Endswith_local s -> if local then check_ending s path else false
| Startswith_local s -> if local then check_start s path else false
| Noslash s -> fnmatch false s (basename path)
| Complex s -> fnmatch true s (string_of_path path)
| Nowildcard (s, l) ->
if l = path_length path then
fnmatch true s (string_of_path path)
else false
let glob_matches local fod_patt path isdir =
let glob_matches_fod local patt path = match patt with
Simple s -> s = basename path
| Simple_local s -> if local then s = basename path else false
| Endswith s -> check_ending s path
| Endswith_local s -> if local then check_ending s path else false
| Startswith_local s -> if local then check_start s path else false
| Noslash s -> fnmatch false s (basename path)
| Complex s -> fnmatch true s (string_of_path path)
| Nowildcard (s, l) ->
if l = path_length path then
fnmatch true s (string_of_path path)
else false
in match fod_patt with
(Dir_Only, patt) -> if isdir then glob_matches_fod local patt path else false
| (File_Or_Dir, patt) -> glob_matches_fod local patt path

let path_of_ign_info t = String.concat "/" (List.rev (List.map fst t))

let is_ignored ?(debug=false) t fname =
let is_ignored ?(debug=false) t fname isdir =
let rec aux local path = function
| [] -> false
| (dname, globs)::tl as t ->
let ign = List.fold_left
(fun s (ty, patt) ->
if glob_matches local patt path then
if glob_matches local patt path isdir then
(match ty with
Accept ->
if debug then
Expand Down

0 comments on commit 8dde54b

Please sign in to comment.