Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file compare_core.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376openCoreopenImportmoduleFile=struct(** [displayed_name <> real_name] when the config specifies prev_alt or next_alt. *)typet={displayed_name:string;real_name:string}letappendtstring={displayed_name=t.displayed_name^/string;real_name=t.real_name^/string};;letof_real(config:Configuration.t)whichreal_name=letconfig_displayed_name=matchwhichwith|`Prev->config.prev_alt|`Next->config.next_altin{real_name;displayed_name=Option.valueconfig_displayed_name~default:real_name};;endletlines_of_contentscontents=letlines=Array.of_list(String.split_linescontents)inlethas_trailing_newline=letlength=String.lengthcontentsiniflength=0||Char.equalcontents.[length-1]'\n'then`With_trailing_newlineelse`Missing_trailing_newlineinlines,has_trailing_newline;;let%test_unit_=lettestcontents~expect=[%test_result:stringarray*[`With_trailing_newline|`Missing_trailing_newline]](lines_of_contentscontents)~expectintest""~expect:([||],`With_trailing_newline);test"hello"~expect:([|"hello"|],`Missing_trailing_newline);test"hello\nworld"~expect:([|"hello";"world"|],`Missing_trailing_newline);test"hello\nworld\n"~expect:([|"hello";"world"|],`With_trailing_newline);;(* Returns a Hunk.t list, ready to be printed *)letcompare_lines(config:Configuration.t)~prev~next=(* Create the diff *)letcontext=config.contextinletkeep_ws=config.keep_wsinletsplit_long_lines=config.split_long_linesinletline_big_enough=config.line_big_enoughinlethunks=lettransform=ifkeep_wsthenFn.idelsePatdiff_core.remove_wsin(* Use external compare program? *)matchconfig.ext_cmpwith|None->Patience_diff.String.get_hunks~transform~context~big_enough:line_big_enough~prev~next|Someprog->letcomparexy=letcmd=sprintf"%s %S %S"progxyinmatchUnix.systemcmdwith|Ok()->0|Error(`Exit_non_zero1)->1|Error_->failwithf"External compare %S failed!"prog()inletmoduleP=Patience_diff.Make(structtypet=string[@@derivingsexp]lethash=String.hashletcompare=compareend)inP.get_hunks~transform~context~big_enough:line_big_enough~prev~nextinlethunks=matchconfig.float_tolerancewith|None->hunks|Sometolerance->Float_tolerance.applyhunkstolerance~contextin(* Refine if desired *)ifconfig.unrefinedthen(* Turn `Replace ranges into `Prev and `Next ranges.
`Replace's would otherwise be later interpreted as refined output *)Patience_diff.Hunks.unifiedhunkselse(letrules=config.rulesinletoutput=config.outputinletproduce_unified_lines=config.produce_unified_linesinletinterleave=config.interleaveinletword_big_enough=config.word_big_enoughinPatdiff_core.refine~rules~output~keep_ws~produce_unified_lines~split_long_lines~interleavehunks~word_big_enough);;letwarn_if_no_trailing_newline~warn_if_no_trailing_newline_in_both(prev_file_newline,prev_file)(next_file_newline,next_file)=letwarn=eprintf"No newline at the end of %s\n%!"inmatchprev_file_newline,next_file_newlinewith|`With_trailing_newline,`With_trailing_newline->()|`With_trailing_newline,`Missing_trailing_newline->warnnext_file|`Missing_trailing_newline,`With_trailing_newline->warnprev_file|`Missing_trailing_newline,`Missing_trailing_newline->ifwarn_if_no_trailing_newline_in_boththen(warnprev_file;warnnext_file);;(* Returns a Hunk.t list, ready to be printed *)letcompare_files(config:Configuration.t)~(prev_file:File.t)~(next_file:File.t)=letprev=In_channel.read_allprev_file.real_nameinletnext=In_channel.read_allnext_file.real_nameinComparison_result.createconfig~prev:{name=prev_file.displayed_name;text=prev}~next:{name=next_file.displayed_name;text=next}~compare_assuming_text:(funconfig~prev~next->letprev_lines,prev_file_newline=lines_of_contentsprev.textinletnext_lines,next_file_newline=lines_of_contentsnext.textinwarn_if_no_trailing_newline(prev_file_newline,prev.name)(next_file_newline,next.name)~warn_if_no_trailing_newline_in_both:config.warn_if_no_trailing_newline_in_both;compare_linesconfig~prev:prev_lines~next:next_lines);;letbinary_different_message~(config:Configuration.t)~prev_file~prev_is_binary~next_file~next_is_binary=matchconfig.location_stylewith|Diff->sprintf"Files %s%s and %s%s differ"prev_file(ifprev_is_binarythen" (binary)"else"")next_file(ifnext_is_binarythen" (binary)"else"")|Omake->String.concat[error_message_start~file:prev_file~line:1;"\n";" File \"";next_file;"\"\n";" binary files differ\n"];;(* Print hunks to stdout *)letprinthunks~(prev_file:File.t)~(next_file:File.t)~(config:Configuration.t)=ifComparison_result.has_no_diffhunksthen(ifconfig.double_checkthen(matchUnix.system(sprintf"cmp -s %s %s"prev_file.real_namenext_file.real_name)with|Ok()->()|Error(`Exit_non_zero1)->printf"There are no differences except those filtered by your settings\n%!"|Error_->()))elseif(* Only print if -quiet is not set *)notconfig.quietthen(letoutput=config.outputinletrules=config.rulesinmatchhunkswith|Binary_same->assertfalse|Binary_different{prev_is_binary;next_is_binary}->Printf.printf"%s\n"(binary_different_message~config~prev_file:prev_file.displayed_name~prev_is_binary~next_file:next_file.displayed_name~next_is_binary)|Hunkshunks->Patdiff_core.printhunks~prev_file:prev_file.displayed_name~next_file:next_file.displayed_name~output~rules~location_style:config.location_style);;letdiff_filesconfig~prev_file~next_file=letprev_file=File.of_realconfig`Prevprev_fileinletnext_file=File.of_realconfig`Nextnext_fileinlethunks=compare_files~prev_file~next_fileconfiginprinthunks~prev_file~next_file~config;ifComparison_result.has_no_diffhunksthen`Sameelse`Different;;letdiff_strings?print_global_header(config:Configuration.t)~(prev:Patdiff_core.diff_input)~(next:Patdiff_core.diff_input)=letlines{Patdiff_core.name=_;text}=String.split_linestext|>Array.of_listinlethunks=Comparison_result.createconfig~prev~next~compare_assuming_text:(funconfig~prev~next->compare_linesconfig~prev:(linesprev)~next:(linesnext))inifComparison_result.has_no_diffhunksthen`Sameelse`Different(matchhunkswith|Binary_same->assertfalse|Binary_different{prev_is_binary;next_is_binary}->binary_different_message~config~prev_file:prev.name~prev_is_binary~next_file:next.name~next_is_binary|Hunkshunks->Patdiff_core.output_to_stringhunks?print_global_header~file_names:(prev.name,next.name)~output:config.output~rules:config.rules~location_style:config.location_style);;letis_regpath=matchUnix.statpathwith|{st_kind=S_REG;_}->true|_->false;;letis_dirpath=matchUnix.statpathwith|{st_kind=S_DIR;_}->true|_->false;;letrecdiff_dirs_internal(config:Configuration.t)~(prev_dir:File.t)~(next_dir:File.t)~file_filter=assert(is_dirprev_dir.real_name);assert(is_dirnext_dir.real_name);letset_of_dir(dir:File.t)=(* Get a list of files for this directory only; do not descend farther
(We recursively call diff_dirs later if we need to descend.) *)letfile_filter=matchfile_filterwith|None->Fn.consttrue|Somefile_filter->file_filterinSys.ls_dirdir.real_name|>List.filter~f:(funx->letx=dir.real_name^/xinmatchUnix.statxwith|exceptionUnix.Unix_error(ENOENT,_,_)->(* If the file disappeared during listing, let's pretend it didn't exist.
This is important when the file is [-exclude]d because we don't want to create
noise for excluded files, but it's also not too bad if the file is [-include]d
*)false|stats->file_filter(x,stats))|>String.Set.of_listinletprev_set=set_of_dirprev_dirinletnext_set=set_of_dirnext_dirin(* Get unique files *)letunion=Set.unionprev_setnext_setinletprev_uniques=Set.diffunionnext_setinletnext_uniques=Set.diffunionprev_setinlethandle_uniquewhichfile~(dir:File.t)=printf"Only in %s: %s\n%!"dir.displayed_namefile;(* Diff unique files against /dev/null, if desired *)ifnotconfig.mask_uniquesthen(letpath=dir.real_name^/fileinifis_regpaththen(letdiff=diff_filesconfiginletnull="/dev/null"inmatchwhichwith|`Prev->ignore(diff~prev_file:path~next_file:null:[`Different|`Same])|`Next->ignore(diff~prev_file:null~next_file:path:[`Different|`Same])))inSet.iterprev_uniques~f:(handle_unique`Prev~dir:prev_dir);Set.iternext_uniques~f:(handle_unique`Next~dir:next_dir);(* Get differences *)letinter=Set.interprev_setnext_setinletexit_code=ref`Sameinletdifffile=letprev_file=File.appendprev_dirfileinletnext_file=File.appendnext_dirfileinifis_regprev_file.real_name&&is_regnext_file.real_namethen(lethunks=compare_files~prev_file~next_fileconfiginifnot(Comparison_result.has_no_diffhunks)then(exit_code:=`Different;(* Print the diff if not -quiet *)matchconfig.quietwith|false->printhunks~prev_file~next_file~config|true->printf"Files %s and %s differ\n%!"prev_file.displayed_namenext_file.displayed_name))elseifis_dirprev_file.real_name&&is_dirnext_file.real_namethenifnotconfig.shallowthen(matchdiff_dirs_internal~prev_dir:prev_file~next_dir:next_fileconfig~file_filterwith|`Same->()|`Different->exit_code:=`Different)elseprintf"Common subdirectories: %s and %s\n%!"prev_file.displayed_namenext_file.displayed_nameelse(exit_code:=`Different;printf"Files %s and %s are not the same type\n%!"prev_file.displayed_namenext_file.displayed_name)inSet.iterinter~f:diff;ifSet.is_emptyprev_uniques&&Set.is_emptynext_uniquesthen!exit_codeelse`Different;;letdiff_dirsconfig~prev_dir~next_dir~file_filter=ifnot(is_dirprev_dir)theninvalid_argf"diff_dirs: prev_dir '%s' is not a directory"prev_dir();ifnot(is_dirnext_dir)theninvalid_argf"diff_dirs: next_dir '%s' is not a directory"next_dir();letprev_dir=File.of_realconfig`Prevprev_dirinletnext_dir=File.of_realconfig`Nextnext_dirindiff_dirs_internalconfig~prev_dir~next_dir~file_filter;;