2 * Simple value database. The entries in the database have
3 * the following format.
7 * - Magic number (16 bytes)
8 * - Digest (used on the source file, for up-to-date info)
12 * - There is at most one entry for each host/field label.
14 * If the magic number doesn't match, then the entry is
15 * out-of-date, and should be replaced.
17 * In some cases, the hostname doesn't matter. Even so, if there
18 * is an entry with the current hostname, and the magic number
19 * doesn't match, it is out-of-date.
21 * NOTE: This has been updated to allowed for key-value pairs
22 * in the header. It looks pretty dumb, but I (jyh) want to keep
23 * the file format backward-compatible. So we stuff all the key/value
24 * pairs in the magic number.
26 * ----------------------------------------------------------------
29 * Copyright (C) 2004-2007 Mojave Group, California Institute of Technology and
30 * HRL Laboratories, LLC
32 * This library is free software; you can redistribute it and/or
33 * modify it under the terms of the GNU Lesser General Public
34 * License as published by the Free Software Foundation,
35 * version 2.1 of the License.
37 * This library is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
40 * Lesser General Public License for more details.
42 * You should have received a copy of the GNU Lesser General Public
43 * License along with this library; if not, write to the Free Software
44 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
46 * Additional permission is given to link this library with the
47 * OpenSSL project's "OpenSSL" library, and with the OCaml runtime,
48 * and you may distribute the linked executables. See the file
49 * LICENSE.libmojave for more details.
51 * Author: Jason Hickey @email{jyh@cs.caltech.edu}
52 * Modified By: Aleksey Nogin @email{anogin@hrl.com}
61 debug_description
= "Display debugging information for marshaling operations";
65 type t
= Unix.file_descr
70 type hostname
= string
71 type named_value
= string * string
73 type entry_pred
= tag
-> named_value list
-> hostname
-> digest
-> bool
75 let first_entry_tag = 1000
78 * Some kinds of entries are host-independent.
85 * Codes during unmarshaling.
92 * Codes during removal.
102 let magic = 0x56e50f8b
107 let hostname = Unix.gethostname
()
108 let digest_length = 16
115 * Win32 doesn't have a general truncate, so seek and truncate.
117 let seek_and_truncate fd pos
=
118 let _ = Unix.lseek fd pos
Unix.SEEK_SET
in
119 Lm_unix_util.ftruncate fd
123 * When an entry is removed, copy the remaining parts of
128 let file_shift fd pos1 pos2
=
129 let buf = String.create
bufsize in
130 let rec copy pos1 pos2
=
131 let _ = Unix.lseek fd pos2
Unix.SEEK_SET
in
132 let amount = Unix.read fd
buf 0 bufsize in
134 let _ = Unix.lseek fd pos1
Unix.SEEK_SET
in
135 assert (Unix.write fd
buf 0 amount = amount);
136 copy (pos1
+ amount) (pos2
+ amount)
140 let pos = copy pos1 pos2
in
141 seek_and_truncate fd
pos;
142 ignore
(Unix.lseek fd pos1
Unix.SEEK_SET
)
145 * If some kind of error happens while removing an entry,
146 * truncate the file at this point.
148 let remove_entry fd
pos off
=
149 try file_shift fd
pos off
with
151 seek_and_truncate fd
pos
156 let unmarshal_magic inx
=
157 try input_binary_int inx
= magic with
161 let unmarshal_tag inx
=
164 let unmarshal_digest inx
=
165 let s = String.create
digest_length in
166 really_input inx
s 0 digest_length;
169 let unmarshal_string inx
=
170 let len = input_binary_int inx
in
171 if len < 0 || len >= 1024 then
172 raise
(Failure
"unmarshal_string")
174 let s = String.create
len in
175 really_input inx
s 0 len;
178 let unmarshal_strings_old inx
=
179 let magic = unmarshal_string inx
in
182 let unmarshal_strings_new inx
=
183 (* Total size of all the entries *)
184 let _ = input_binary_int inx
in
186 (* Number of key/value pairs *)
187 let len = input_binary_int inx
in
188 (* Read the key/value pairs *)
189 if len < 0 || len >= 1024 then
190 raise
(Failure
"unmarshal_string")
192 let rec loop strings i
=
196 let key = unmarshal_string inx
in
197 let value = unmarshal_string inx
in
198 loop ((key, value) :: strings
) (i
+ 1)
202 let unmarshal_strings inx tag
=
203 if tag
< first_entry_tag then
204 unmarshal_strings_old inx
206 unmarshal_strings_new inx
209 * Search for the appropriate entry.
211 let find_entry fd filename test
=
212 let _ = Unix.lseek fd
0 Unix.SEEK_SET
in
213 let inx = Unix.in_channel_of_descr fd
in
214 let head = String.create
Marshal.header_size
in
216 (* Find the appropriate entry *)
217 let unmarshal_entry () =
219 let tag = unmarshal_tag inx in
220 let host = unmarshal_string inx in
221 let strings = unmarshal_strings inx tag in
222 let digest = unmarshal_digest inx in
223 if test
tag strings host digest then begin
224 (* Found a matching entry *)
226 eprintf
"@[<v 3>Marshal.from_channel: %s@ save tag/digest: %d/%s@." (**)
228 tag (Lm_string_util.hexify
digest);
229 let x = UnmarshalValue
(Marshal.from_channel
inx) in
231 eprintf
"Marshal.from-channel: done@.";
235 (* Skip over this entry *)
236 let () = really_input
inx head 0 Marshal.header_size
in
237 let size = Marshal.data_size
head 0 in
238 let pos = pos_in
inx + size in
244 * Search through the entries. If an exception is raised,
245 * truncate the file at the start of the entry.
248 let start = pos_in
inx in
250 try unmarshal_entry () with
254 | Invalid_argument
_ ->
256 eprintf
"Lm_db.find: %s: failed@." filename
;
257 seek_and_truncate fd
start;
266 if unmarshal_magic inx then
271 let find fd filename
(tag, host_mode
) magic digest =
272 let test tag'
strings host'
digest'
=
275 tag'
= tag && magic'
= magic && digest'
= digest && (host_mode
= HostIndependent
|| host'
= hostname)
279 find_entry fd filename
test
282 * Remove an entry. Search through the existing entries
283 * to find one with the same tag. If the host is significant,
284 * remove only the entry with the same hostname. Otherwise,
285 * remove the entry with the same magic number.
287 let marshal_magic fd
=
288 seek_and_truncate fd
0;
289 let outx = Unix.out_channel_of_descr fd
in
290 output_binary_int
outx magic;
291 Pervasives.flush
outx
293 let remove_entry fd filename
test =
294 let head = String.create
Marshal.header_size
in
296 (* Find the appropriate entry *)
297 let unmarshal_entry inx =
299 let tag = unmarshal_tag inx in
300 let host = unmarshal_string inx in
301 let strings = unmarshal_strings inx tag in
302 let digest = unmarshal_digest inx in
303 let () = really_input
inx head 0 Marshal.header_size
in
304 let size = Marshal.data_size
head 0 in
305 let pos = pos_in
inx + size in
306 if test tag strings host digest then
315 * Search through the entries. If an exception is raised,
316 * truncate the file at the start of the entry.
319 let start = pos_in
inx in
321 try unmarshal_entry inx with
325 | Invalid_argument
_ ->
330 remove_entry fd
start pos;
331 ignore
(Unix.lseek fd
0 Unix.SEEK_SET
);
332 let inx = Unix.in_channel_of_descr fd
in
338 seek_and_truncate fd
start
340 let _ = Unix.lseek fd
0 Unix.SEEK_SET
in
341 let inx = Unix.in_channel_of_descr fd
in
342 if unmarshal_magic inx then
347 let remove fd filename
(tag, host_mode
) magic =
348 let test tag'
strings host'
digest'
=
351 tag'
= tag && (host'
= hostname || host_mode
= HostIndependent
&& magic'
= magic)
355 remove_entry fd filename
test
359 * Remove any existing entry, and add the new one to the end of the
362 let marshal_tag outx tag =
363 output_binary_int
outx tag
365 let marshal_digest outx digest =
366 assert (String.length
digest = digest_length);
367 Pervasives.output_string
outx digest
369 let marshal_string outx s =
370 let len = String.length
s in
371 output_binary_int
outx len;
372 Pervasives.output_string
outx s
374 let marshal_strings outx sl
=
376 List.fold_left
(fun len (key, value) ->
377 len + String.length
key + String.length
value + 8) 4 sl
379 output_binary_int
outx len;
380 output_binary_int
outx (List.length sl
);
381 List.iter
(fun (key, value) ->
382 marshal_string outx key;
383 marshal_string outx value) sl
385 let marshal_entry fd filename
tag magic_number
digest x =
386 let outx = Unix.out_channel_of_descr fd
in
387 marshal_tag outx tag;
388 marshal_string outx hostname;
389 marshal_string outx magic_number
;
390 marshal_digest outx digest;
392 eprintf
"@[<v 3>Marshal.to_channel: %s@ tag/digest: %d/%s@]@." (**)
394 tag (Lm_string_util.hexify
digest);
395 Marshal.to_channel
outx x [];
397 eprintf
"Marshal.to_channel: %s: done@." filename
;
398 Pervasives.flush
outx
400 let add fd filename
((code, _) as tag) magic digest x =
401 remove fd filename
tag magic;
402 marshal_entry fd filename
code magic digest x
404 let append_entry fd filename
tag strings digest x =
405 let _ = Unix.lseek fd
0 Unix.SEEK_END
in
406 let outx = Unix.out_channel_of_descr fd
in
407 marshal_tag outx tag;
408 marshal_string outx hostname;
409 marshal_strings outx strings;
410 marshal_digest outx digest;
412 eprintf
"@[<v 3>Marshal.to_channel: %s@ tag/digest: %d/%s@]@." (**)
414 tag (Lm_string_util.hexify
digest);
415 Marshal.to_channel
outx x [];
417 eprintf
"Marshal.to_channel: %s: done@." filename
;
418 Pervasives.flush
outx