current commit
260927.1.1 · fix: explicit path argument silently ignored when stdin is not a tty
#include <hypergrep/constants.hpp>
#include <hypergrep/directory_search.hpp>
#include <hypergrep/file_search.hpp>
#include <hypergrep/git_index_search.hpp>
#include <hypergrep/print_help.hpp>

void perform_search(std::string &pattern, std::string_view path,
                    argparse::ArgumentParser &program, bool path_explicitly_provided) {
  if (!path_explicitly_provided && !isatty(fileno(stdin))) {
    // Program was called from a pipe

    file_search s(pattern, program);
    std::string line;
    std::size_t current_line_number{1};
    while (std::getline(std::cin, line)) {
      // Process line here
      bool break_loop{false};
      s.scan_line(line, current_line_number, break_loop);
      if (break_loop) {
        break;
      }
    }
  } else {
    if (std::filesystem::is_regular_file(path)) {
      static file_search s(pattern, program);
      s.run(path);
    } else {

      const auto ignore_gitindex = program.get<bool>("--ignore-gitindex");
      if (ignore_gitindex) {
        // Just run directory search
        static directory_search s(pattern, path, program);
        s.run(path);
      } else {
        // Check if search path is a git repo
        const auto current_path = std::filesystem::current_path();

        if (std::filesystem::exists(std::filesystem::path(path) / ".git")) {
          if (chdir(path.data()) == 0) {
            static git_index_search s(pattern, current_path, program);
            s.run(".");
            if (chdir(current_path.c_str()) != 0) {
              throw std::runtime_error("Failed to restore path");
            }
          }
        } else {
          directory_search s(pattern, path, program);
          s.run(path);
        }
      }
    }
  }
}

int main(int argc, char **argv) {

  argparse::ArgumentParser program("hg", VERSION.data(),
                                   argparse::default_arguments::none);

  program.add_argument("-h", "--help")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("-v", "--version")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("-b", "--byte-offset")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("--column").default_value(false).implicit_value(true);

  program.add_argument("-c", "--count")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("--count-matches")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("-e", "--regexp")
      .default_value<std::vector<std::string>>({})
      .append();

  program.add_argument("-f", "--file").append();

  program.add_argument("--files").default_value(false).implicit_value(true);

  program.add_argument("--filter");

  program.add_argument("-F", "--fixed-strings")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("--hidden").default_value(false).implicit_value(true);

  program.add_argument("-i", "--ignore-case")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("--ignore-gitindex")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("--ignore-submodules")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("--include-zero")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("-I", "--no-filename")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("-l", "--files-with-matches")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("-M", "--max-columns").scan<'d', std::size_t>();

  program.add_argument("--max-filesize");

  program.add_argument("-n", "--line-number")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("-N", "--no-line-number")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("-o", "--only-matching")
      .default_value(false)
      .implicit_value(true);

  program.add_argument("--trim").default_value(false).implicit_value(true);

  program.add_argument("--ucp").default_value(false).implicit_value(true);

  program.add_argument("-w", "--word-regexp")
      .default_value(false)
      .implicit_value(true);

  const auto max_concurrency = std::thread::hardware_concurrency();
  const auto default_num_threads =
      max_concurrency > 1 ? max_concurrency - 1 : 1;
  program.add_argument("-j", "--threads")
      .help("The number of threads to use")
      .default_value(default_num_threads)
      .scan<'d', unsigned>();

  program.add_argument("patterns_and_paths")
      .default_value(std::vector<std::string>{})
      .remaining();

  try {
    program.parse_args(argc, argv);
  } catch (const std::runtime_error &err) {
    std::cerr << err.what() << std::endl;
    std::cerr << "\nFor more information try --help\n";
    return 1;
  }

  if (program.is_used("-h")) {
    print_help();
    return 0;
  } else if (program.is_used("-v")) {
    fmt::print("{}\n", VERSION);
    return 0;
  }

  // If -f,--files,--regexp is NOT used,
  // then, a pattern argument is required
  const auto pattern_file_provided = program.is_used("-f");
  const auto files_used = program.get<bool>("--files");
  const auto regexp_used = program.is_used("-e");

  if (pattern_file_provided || files_used || regexp_used) {
    // Treat everything in patterns_and_paths
    // as a list of paths
    //
    // If empty, just search "."
    auto empty_pattern = std::string{};
    auto paths = program.get<std::vector<std::string>>("patterns_and_paths");
    if (paths.empty()) {
      perform_search(empty_pattern, ".", program, /* path_explicitly_provided */ false);
    } else {
      for (const auto &path : paths) {
        perform_search(empty_pattern, path, program, /* path_explicitly_provided */ true);
      }
    }
  } else {
    // Treat first in patterns_and_paths
    // as the pattern
    //
    // The rest are paths to process
    //
    // If size == 1 (i.e. just a pattern provided), just search "."
    auto patterns_and_paths =
        program.get<std::vector<std::string>>("patterns_and_paths");
    const auto size = patterns_and_paths.size();

    if (size == 0) {
      std::cerr << "1 argument(s) expected for <PATTERN>. 0 provided."
                << std::endl;
      std::cerr << "\nFor more information try --help\n";
      return 1;
    }

    auto &pattern = patterns_and_paths[0];

    if (size == 1) {
      // Path not provided
      // Default to current directory
      perform_search(pattern, ".", program, /* path_explicitly_provided */ false);
    } else {
      for (std::size_t i = 1; i < patterns_and_paths.size(); ++i) {
        perform_search(pattern, patterns_and_paths[i], program, /* path_explicitly_provided */ true);
      }
    }
  }
  return 0;
}