# # patch "database.ml" # from [472e2cfda6c6411367ea02530bc44962a0f58577] # to [417f553c4ad5b0bda0aa848e7b4d952929f28171] # # patch "main.ml" # from [3feaecce13b222addbbf8fd459a0738d7e2c36b0] # to [5fe68ce94e77ee1350c7822b4eb69e38cd5f94a5] # # patch "unidiff.ml" # from [27d78ed2191d1fb186ad09637874b4832c59b480] # to [268beb3104432d9e384bf1c5daa74f6735800d36] # # patch "view.ml" # from [b265c6cf6b9c7c26c9e7e41b61534b6dcb22b22c] # to [75181c9100572d94d6cca7e2e746179809e53bc4] # --- database.ml +++ database.ml @@ -259,37 +259,24 @@ "--revision=" ^ new_id ; "diff" ] in if Viz_misc.debug "exec" then Printf.eprintf "### exec: Running '%s'\n%!" (String.concat " " cmd) ; - let error fmt = - Printf.kprintf (fun s -> cb (`SUB_PROC_ERROR s)) fmt in try status#push "Running monotone ..." ; Subprocess.spawn_out - ~encoding:`LOCALE ~cmd + ~encoding:`NONE ~cmd ~reap_callback:status#pop (fun ~exceptions ~stdout ~stderr status -> - if status <> 0 then - if List.exists - (function - | Glib.Convert.Error (Glib.Convert.ILLEGAL_SEQUENCE, _) -> true - | _ -> false) - exceptions - then - error - begin - let (is_utf8, _) = Glib.Convert.get_charset () in - if is_utf8 - then format_of_string "Monotone output is not valid UTF-8" - else format_of_string "Could not convert monotone output to UTF-8" - end - else - if stderr = "" - then - error "Monotone exited with status %d:\n%s" status - (String.concat "\n" (List.map Printexc.to_string exceptions)) - else - error "Monotone error:\n%s" stderr + if status = 0 + then + cb (`DIFF stdout) else - cb (`DIFF stdout)) + let error fmt = + Printf.kprintf (fun s -> cb (`SUB_PROC_ERROR s)) fmt in + if stderr = "" + then + error "Monotone exited with status %d:\n%s" status + (String.concat "\n" (List.map Printexc.to_string exceptions)) + else + error "Monotone error:\n%s" stderr) with Gspawn.Error (_, msg) -> Viz_types.errorf "Could not execute monotone:\n%s" msg --- main.ml +++ main.ml @@ -44,16 +44,27 @@ with Failure _ -> [] in match may_assoc "database" options with | None -> MTopt_none - | Some db -> - match may_assoc "branch" options with - | None -> MTopt_db db - | Some branch -> MTopt_full (db, branch) + | Some db_raw -> + try + let db = Glib.Convert.filename_from_utf8 db_raw in + match may_assoc "branch" options with + | None -> MTopt_db db + | Some branch -> MTopt_full (db, branch) + with Glib.Convert.Error _ -> + MTopt_none let parse_options args = match args with - | db :: [] | db :: "" :: _ -> MTopt_db db - | db :: branch :: _ -> MTopt_full (db, branch) - | [] -> parse_MT_options () + | [] -> + parse_MT_options () + | db :: [] | db :: "" :: _ -> + MTopt_db db + | db :: branch_raw :: _ -> + try + let branch = Glib.Convert.locale_to_utf8 branch_raw in + MTopt_full (db, branch) + with Glib.Convert.Error _ -> + MTopt_db db let parse_cli () = let anons = ref Q.empty in --- unidiff.ml +++ unidiff.ml @@ -101,7 +101,7 @@ Viz_types.errorf "Could not write monotone diff output to '%s'" f)) ; s -let view_diff ?parent (junk_end, tags_coords) text = +let view_diff ?parent (junk_end, tags_coords) text orig_text = let window = GWindow.dialog ~no_separator:true ?parent ~title:"Monotone diff output" @@ -110,7 +110,7 @@ window#add_button_stock `SAVE `SAVE ; window#add_button_stock `CLOSE `CLOSE ; window#set_default_response `CLOSE ; - let s = lazy (save_dialog window text) in + let s = lazy (save_dialog window orig_text) in ignore (window#connect#response (function | `CLOSE | `DELETE_EVENT -> window#destroy () | `SAVE -> (Lazy.force s)#present () )) ; @@ -163,10 +163,51 @@ window#misc#show () + +let replacement_char = '?' +let careful_convert_ascii o = + let s = String.copy o in + for i = 0 to String.length s - 1 do + if int_of_char s.[i] >= 0x80 + then s.[i] <- replacement_char + done ; + s +let careful_convert_utf8 o = + let s = String.copy o in + let len = String.length s in + let pos = ref 0 in + while !pos < len do + let prev_pos = !pos in + try ignore (Glib.Utf8.to_unichar_validated s ~pos) + with + | Glib.Convert.Error (Glib.Convert.ILLEGAL_SEQUENCE, _) -> + pos := prev_pos ; + s.[!pos] <- replacement_char ; + incr pos + | Glib.Convert.Error (Glib.Convert.PARTIAL_INPUT, _) -> + String.fill s prev_pos (len - prev_pos) replacement_char ; + pos := len + done ; + s + +let utf8ize = + let (is_utf8, _) = Glib.Convert.get_charset () in + if not is_utf8 + then fun s -> + try Glib.Convert.locale_to_utf8 s + with Glib.Convert.Error _ -> + careful_convert_ascii s + else fun s -> + if Glib.Utf8.validate s + then s + else careful_convert_utf8 s + + let view ~parent text = let parent = GWindow.toplevel parent in - try - view_diff ?parent (analyze_diff_output text) text + try + let display_text = utf8ize text in + view_diff ?parent (analyze_diff_output display_text) display_text text with Not_found -> let d = GWindow.message_dialog --- view.ml +++ view.ml @@ -1,18 +1,9 @@ open Viz_misc open Viz_types open Revision_types +let valid_utf8 = Glib.Utf8.validate -let utf8ize = - let fallback = "" in - let (is_utf8, _) = Glib.Convert.get_charset () in - if not is_utf8 - then fun s -> - try Glib.Convert.locale_to_utf8 s - with Glib.Convert.Error _ -> fallback - else fun s -> - (if Glib.Utf8.validate s then s else fallback) - let wrap_in_scroll_window packing = let sw = GBin.scrolled_window ~hpolicy:`AUTOMATIC @@ -274,12 +265,14 @@ List.iter (fun change -> let text = text_of_delta_type change in + (* monotone uses UTF-8 internaly *) + assert (valid_utf8 text) ; if text <> "" then let row = m#append ~parent () in may (m#set ~row ~column:i.revision_c_type) (stock_of_delta_type change) ; - m#set ~row ~column:i.revision_c_file (utf8ize text)) + m#set ~row ~column:i.revision_c_file text) change_set) data.revision_set ; i.revision_view#set_model (Some i.revision_model#coerce) ; @@ -296,7 +289,12 @@ let row = m#append () in let c_disp = String.capitalize c.c_name ^ ":" in m#set ~row ~column:i.cert_c_name c_disp ; - m#set ~row ~column:i.cert_c_value (utf8ize c.c_value) ; + (* cert values are either binary or valid utf8 *) + let c_value_disp = + if valid_utf8 c.c_value + then c.c_value + else "" in + m#set ~row ~column:i.cert_c_value c_value_disp ; m#set ~row ~column:i.cert_c_signer c.c_signer_id ; m#set ~row ~column:i.cert_c_sig c.c_signature) data.certs ; @@ -402,7 +400,9 @@ List.iter (fun b -> let row = model#append () in - model#set ~row ~column (Glib.Markup.escape_text (utf8ize b))) + (* branch names are UTF-8 compatible *) + assert (valid_utf8 b) ; + model#set ~row ~column (Glib.Markup.escape_text b)) br) let set_branch { selector = s } b = @@ -1025,6 +1025,8 @@ let open_db v fname branch = + (* fname should be in filesystem encoding, + branch shoud be UTF-8 *) close v ; try let db = Database.open_db fname in