1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
(* approx: proxy server for Debian archive files
Copyright (C) 2008 Eric C. Cooper <ecc@cmu.edu>
Released under the GNU General Public License *)
open Util
let get_line chan = try Some (input_line chan) with End_of_file -> None
let output_line chan line = output_string chan line; output_char chan '\n'
(* Apply a function to lines m through n *)
let iter_lines proc m n chan =
let rec loop i =
if i <= n then
match get_line chan with
| Some line -> proc line; loop (i + 1)
| None -> failwith ("EOF while scanning to line " ^ string_of_int n)
in
loop m
(* Apply a function to all remaining lines *)
let iter_eof proc chan =
let rec loop () =
match get_line chan with
| Some line -> proc line; loop ()
| None -> ()
in
loop ()
let copy_lines m n ic oc = iter_lines (output_line oc) m n ic
let delete_lines = iter_lines ignore
(* The following operators implement the corresponding ed commands
and update the current input line number *)
let append lines n ic oc cur =
copy_lines cur n ic oc;
List.iter (output_line oc) lines;
n + 1
let change lines m n ic oc cur =
copy_lines cur (m - 1) ic oc;
delete_lines m n ic;
List.iter (output_line oc) lines;
n + 1
let delete = change []
let copy_tail ic oc cur =
iter_eof (output_line oc) ic;
0
(* Collect lines until a terminating "." line is seen *)
let get_lines chan =
let rec loop lines =
match get_line chan with
| Some "." -> lines
| Some line -> loop (line :: lines)
| None -> failwith "EOF occurred before terminating \".\""
in
List.rev (loop [])
let range_of_string str =
try
let i = String.index str ',' in
let start = int_of_string (substring str ~until: i) in
let stop = int_of_string (substring str ~from: (i + 1)) in
start, stop
with Not_found ->
let n = int_of_string str in
n, n
(* Ed commands are represented as operators on the
input channel, output channel, and current line number.
When applied, each operator returns the updated line number. *)
type t = in_channel -> out_channel -> int -> int
(* Translate an ed command into an operator.
Additional lines are consumed from the channel
in the case of append and change commands. *)
let parse_line chan line =
let last = String.length line - 1 in
try
let (m, n) = range_of_string (String.sub line 0 last) in
match line.[last] with
| 'a' -> assert (m = n); append (get_lines chan) m
| 'c' -> change (get_lines chan) m n
| 'd' -> delete m n
| _ -> raise Exit
with _ -> failwith ("malformed ed command: " ^ line)
(* Parse an input channel containing ed commands.
"diff --ed" produces commands in decreasing line-number order;
this function effectively reverses that order as it composes
the operators. *)
let parse chan =
let compose f g ic oc cur = g ic oc (f ic oc cur) in
let rec loop op =
match get_line chan with
| Some line -> loop (compose (parse_line chan line) op)
| None -> op
in
loop copy_tail
let apply cmds ic oc = ignore (cmds ic oc 1)
|