open Pcre2

let parse_args () =
  let quick = ref false
  and first = ref false
  and ignore_case = ref false
  and offset = ref 0
  and pat = ref None
  and substr = ref None in

  let q =
    ( "-q",
      Arg.Set quick,
      "Quick replacement. Interpretes substitution as plain text." )
  and f = ("-f", Arg.Set first, "Replace first occurrence in line only.")
  and i = ("-i", Arg.Set ignore_case, "Ignore case.")
  and ofs =
    ("-ofs", Arg.Int (fun n -> offset := n), "Start matching at column n.")
  and usage =
    "Usage: subst [-q] [-f] [-i] [-ofs offset] pattern substitution\n\n\
     Reads lines from standard input and replaces occurrences of\n\
     the PERL-style regular expression \"pattern\" with \"substitution\",\n\
     printing the result to standard output.\n\
     In default mode the contents of \"substitution\" will be interpreted\n\
     similarly to its equivalent in PERL.\n"
  and anon_arg arg =
    match (!pat, !substr) with
    | None, _ -> pat := Some arg
    | _, None -> substr := Some arg
    | _ -> raise (Arg.Bad "too many arguments!")
  in

  let args = [ q; f; i; ofs ] in
  Arg.parse args anon_arg usage;

  let flags = if !ignore_case then [ `CASELESS ] else [] in

  let rex, sstr =
    match (!pat, !substr) with
    | Some rex, Some sstr -> (regexp ~flags rex, sstr)
    | _ ->
        prerr_endline (Sys.argv.(0) ^ ": not enough arguments!");
        Arg.usage args usage;
        exit 1
  in

  match (!quick, !first) with
  | false, false -> fun s -> replace ~rex ~pos:!offset ~templ:sstr s
  | true, false -> fun s -> qreplace ~rex ~pos:!offset ~templ:sstr s
  | false, true -> fun s -> replace_first ~rex ~pos:!offset ~templ:sstr s
  | true, true -> fun s -> qreplace_first ~rex ~pos:!offset ~templ:sstr s

let _ =
  let substitute = parse_args () in
  foreach_line (fun line ->
      try print_endline (substitute line)
      with Invalid_argument _ -> print_endline line)
