let find ?(follow = Skip) ?match_compile tst fln exec user_acc =

  let user_test =
    compile_filter ?match_compile tst
  in

  let process_file ((user_acc, already_read) as acc) st fln = 
    if user_test ~pre_stat:st fln then
      (exec user_acc fln), already_read
    else
      acc
  in

  let skip_action =
    match follow with 
      | Skip | AskFollow _ | Follow ->
          ignore 
      | SkipInform f ->
          f
  in

  let should_skip fln already_followed =
    match follow with 
      | Skip | SkipInform _ -> 
          true
      | AskFollow f -> 
          if not already_followed then
            (f fln) 
          else
            true
      | Follow -> 
          if already_followed then
            raise (RecursiveLink fln)
          else
            false
  in

  let rec find_aux acc fln =
    try
      (
        let st = 
          stat fln
        in
          if st.kind = Dir then
            (
              if st.is_link then
                (
                  let (user_acc, dir_links) =
                    acc
                  in
                  let cur_link =
                     readlink fln
                  in
                  let dir_links, already_followed = 
                    try
                      (prevent_recursion dir_links cur_link), false
                    with RecursiveLink _ ->
                      dir_links, true
                  in
                  let acc = 
                    user_acc, dir_links
                  in
                    if should_skip fln already_followed then
                      (
                        skip_action fln;
                        acc
                      )
                    else
                      (
                        find_in_dir
                          (process_file acc st fln)
                          fln
                      )
                )
              else
                (
                  find_in_dir
                    (process_file acc st fln)
                    fln
                )
            )
          else
            (
              process_file acc st fln
            )
      )
    with FileDoesntExist _ ->
      acc

  and find_in_dir acc drn = 
    Array.fold_left
      (fun acc rfln ->
         if is_parent rfln || is_current rfln then
           acc
         else
           find_aux acc (concat drn rfln))
      acc
      (Sys.readdir drn)
  in

  let user_acc, _ = 
    find_aux 
      (user_acc, SetFilename.empty) 
      (reduce fln)
  in
    user_acc