Plasma GitLab Archive
Projects Blog Knowledge

(* $Id: pxp_reader.ml,v 1.20 2002/03/08 14:37:15 gerd Exp $
 * ----------------------------------------------------------------------
 * PXP: The polymorphic XML parser for Objective Caml.
 * Copyright by Gerd Stolpmann. See LICENSE for details.
 *)

open Pxp_types;;
exception Not_competent;;
exception Not_resolvable of exn;;

class type resolver =
  object
    method init_rep_encoding : rep_encoding -> unit
    method init_warner : collect_warnings -> unit
    method rep_encoding : rep_encoding
    method open_in : ext_id -> Lexing.lexbuf
    method close_in : unit
    method close_all : unit
    method change_encoding : string -> unit
    method clone : resolver
  end
;;


class virtual resolve_general
 =
  object (self)
    val mutable internal_encoding = `Enc_utf8

    val mutable encoding = `Enc_utf8
    val mutable encoding_requested = false

    val mutable warner = new drop_warnings

    val mutable enc_initialized = false
    val mutable wrn_initialized = false

    val mutable clones = []

    method init_rep_encoding e =
      internal_encoding <- e;
      enc_initialized <- true;

    method init_warner w =
      warner <- w;
      wrn_initialized <- true;

    method rep_encoding = (internal_encoding :> rep_encoding)

(*
    method clone =
      ( {< encoding = `Enc_utf8;
	   encoding_requested = false;
	>}
	: # resolver :> resolver )
*)

    method private warn (k:int) =
      (* Called if a character not representable has been found.
       * k is the character code.
       *)
	if k < 0xd800 or (k >= 0xe000 & k <= 0xfffd) or
	   (k >= 0x10000 & k <= 0x10ffff) then begin
	     warner # warn ("Code point cannot be represented: " ^ string_of_int k);
	   end
	else
	  raise (WF_error("Code point " ^ string_of_int k ^
		    " outside the accepted range of code points"))


    method private autodetect s =
      (* s must be at least 4 bytes long. The slot 'encoding' is
       * set to:
       * "UTF-16-BE": UTF-16/UCS-2 encoding big endian
       * "UTF-16-LE": UTF-16/UCS-2 encoding little endian
       * "UTF-8":     UTF-8 encoding
       *)
      if String.length s < 4 then
	encoding <- `Enc_utf8
      else if String.sub s 0 2 = "\254\255" then
	encoding <- `Enc_utf16
	  (* Note: Netconversion.recode will detect the big endianess, too *)
      else if String.sub s 0 2 = "\255\254" then
	encoding <- `Enc_utf16
	  (* Note: Netconversion.recode will detect the little endianess, too *)
      else
	encoding <- `Enc_utf8


    method private virtual next_string : string -> int -> int -> int
    method private virtual init_in : ext_id -> unit
    method virtual close_in : unit

    method close_all =
      List.iter (fun r -> r # close_in) clones

    method open_in xid =
      assert(enc_initialized && wrn_initialized);

      encoding <- `Enc_utf8;
      encoding_requested <- false;
      self # init_in xid;         (* may raise Not_competent *)
      (* init_in: may already set 'encoding' *)

      let buffer_max = 512 in
      let buffer = String.make buffer_max ' ' in
      let buffer_len = ref 0 in
      let buffer_end = ref false in
      let fillup () =
	if not !buffer_end & !buffer_len < buffer_max then begin
	  let l =
	    self # next_string buffer !buffer_len (buffer_max - !buffer_len) in
	  if l = 0 then
	    buffer_end := true
	  else begin
	    buffer_len := !buffer_len + l
	  end
	end
      in
      let consume n =
	let l = !buffer_len - n in
	if l > 0 then String.blit buffer n buffer 0 l;
	buffer_len := l
      in

      fillup();
      if not encoding_requested then self # autodetect buffer;

      Pxp_lexing.from_function
	(fun s n ->
	   (* TODO: if encoding = internal_encoding, it is possible to
	    * avoid copying buffer to s because s can be directly used
	    * as buffer.
	    *)

	   fillup();
	   if !buffer_len = 0 then
	     0
	   else begin
	     let m_in  = !buffer_len in
	     let m_max = if encoding_requested then n else 1 in
	     let n_in, n_out, encoding' =
	       if encoding = (internal_encoding : rep_encoding :> encoding) &&
	          encoding_requested
	       then begin
		 (* Special case encoding = internal_encoding *)
		 String.blit buffer 0 s 0 m_in;
		 m_in, m_in, encoding
	       end
	       else
		 Netconversion.recode
		   ~in_enc:encoding
		   ~in_buf:buffer
		   ~in_pos:0
		   ~in_len:m_in
		   ~out_enc:(internal_encoding : rep_encoding :> encoding)
		   ~out_buf:s
		   ~out_pos:0
		   ~out_len:n
		   ~max_chars:m_max
		   ~subst:(fun k -> self # warn k; "")
	     in
	     if n_in = 0 then
	       (* An incomplete character at the end of the stream: *)
	       raise Netconversion.Malformed_code;
	       (* failwith "Badly encoded character"; *)
	     encoding <- encoding';
	     consume n_in;
	     assert(n_out <> 0);
	     n_out
	   end)

    method change_encoding enc =
      if not encoding_requested then begin
	if enc <> "" then begin
	  match Netconversion.encoding_of_string enc with
	      `Enc_utf16 ->
		(match encoding with
		     (`Enc_utf16_le | `Enc_utf16_be) -> ()
		   | `Enc_utf16 -> assert false
		   | _ ->
		       raise(WF_error "Encoding of data stream and encoding declaration mismatch")
		)
	    | e ->
		encoding <- e
	end;
	(* else: the autodetected encoding counts *)
	encoding_requested <- true;
      end;
  end
;;


class resolve_read_any_channel ?(close=close_in) ~channel_of_id () =
  object (self)
    inherit resolve_general as super

    val f_open = channel_of_id
    val mutable current_channel = None
    val close = close

    method private init_in (id:ext_id) =
      if current_channel <> None then
	failwith "Pxp_reader.resolve_read_any_channel # init_in";
      let ch, enc_opt = f_open id in       (* may raise Not_competent *)
      begin match enc_opt with
	  None     -> ()
	| Some enc -> encoding <- enc; encoding_requested <- true
      end;
      current_channel <- Some ch;

    method private next_string s ofs len =
      match current_channel with
	  None -> failwith "Pxp_reader.resolve_read_any_channel # next_string"
	| Some ch ->
	    input ch s ofs len

    method close_in =
      match current_channel with
	  None -> ()
	| Some ch ->
	    close ch;
	    current_channel <- None

    method clone =
      let c = new resolve_read_any_channel
		?close:(Some close) ~channel_of_id:f_open () in
      c # init_rep_encoding internal_encoding;
      c # init_warner warner;
      clones <- c :: clones;
      (c :> resolver)

  end
;;


class resolve_read_this_channel1 is_stale ?id ?fixenc ?close ch =

  let getchannel = ref (fun xid -> assert false) in

  object (self)
    inherit resolve_read_any_channel
              ?close
	      ~channel_of_id:(fun xid -> !getchannel xid)
	      ()
	      as super

    val mutable is_stale = is_stale
      (* The channel can only be read once. To avoid that the channel
       * is opened several times, the flag 'is_stale' is set after the
       * first time.
       *)

    val fixid = id
    val fixenc = fixenc
    val fixch = ch

    initializer
      getchannel := self # getchannel

    method private getchannel xid =
      begin match fixid with
	  None -> ()
	| Some bound_xid ->
	    if xid <> bound_xid then raise Not_competent
      end;
      ch, fixenc

    method private init_in (id:ext_id) =
      if is_stale then
	raise Not_competent
      else begin
	super # init_in id;
	is_stale <- true
      end

    method close_in =
      current_channel <- None

    method clone =
      let c = new resolve_read_this_channel1
		is_stale
		?id:fixid ?fixenc:fixenc ?close:(Some close) fixch
      in
      c # init_rep_encoding internal_encoding;
      c # init_warner warner;
      clones <- c :: clones;
      (c :> resolver)

  end
;;


class resolve_read_this_channel =
  resolve_read_this_channel1 false
;;


class resolve_read_any_string ~string_of_id () =
  object (self)
    inherit resolve_general as super

    val f_open = string_of_id
    val mutable current_string = None
    val mutable current_pos    = 0

    method private init_in (id:ext_id) =
      if current_string <> None then
	failwith "Pxp_reader.resolve_read_any_string # init_in";
      let s, enc_opt = f_open id in       (* may raise Not_competent *)
      begin match enc_opt with
	  None     -> ()
	| Some enc -> encoding <- enc; encoding_requested <- true
      end;
      current_string <- Some s;
      current_pos    <- 0;

    method private next_string s ofs len =
      match current_string with
	  None -> failwith "Pxp_reader.resolve_read_any_string # next_string"
	| Some str ->
	    let l = min len (String.length str - current_pos) in
	    String.blit str current_pos s ofs l;
	    current_pos <- current_pos + l;
	    l

    method close_in =
      match current_string with
	  None -> ()
	| Some _ ->
	    current_string <- None

    method clone =
      let c = new resolve_read_any_string ~string_of_id:f_open () in
      c # init_rep_encoding internal_encoding;
      c # init_warner warner;
      clones <- c :: clones;
      (c :> resolver)
  end
;;


class resolve_read_this_string1 is_stale ?id ?fixenc str =

  let getstring = ref (fun xid -> assert false) in

  object (self)
    inherit resolve_read_any_string (fun xid -> !getstring xid) () as super

    val is_stale = is_stale
      (* For some reasons, it is not allowed to open a clone of the resolver
       * a second time when the original resolver is already open.
       *)

    val fixid = id
    val fixenc = fixenc
    val fixstr = str

    initializer
      getstring := self # getstring

    method private getstring xid =
      begin match fixid with
	  None -> ()
	| Some bound_xid ->
	    if xid <> bound_xid then raise Not_competent
      end;
      fixstr, fixenc


    method private init_in (id:ext_id) =
      if is_stale then
	raise Not_competent
      else
	super # init_in id

    method clone =
      let c = new resolve_read_this_string1
		(is_stale or current_string <> None)
		?id:fixid ?fixenc:fixenc fixstr
      in
      c # init_rep_encoding internal_encoding;
      c # init_warner warner;
      clones <- c :: clones;
      (c :> resolver)
  end
;;


class resolve_read_this_string =
  resolve_read_this_string1 false
;;


class resolve_read_url_channel
  ?(base_url = Neturl.null_url)
  ?close
  ~url_of_id
  ~channel_of_url
  ()

  : resolver
  =

  let getchannel = ref (fun xid -> assert false) in

  object (self)
    inherit resolve_read_any_channel
              ?close
	      ~channel_of_id:(fun xid -> !getchannel xid)
	      ()
	      as super

    val base_url = base_url
    val mutable own_url = Neturl.null_url

    val url_of_id = url_of_id
    val channel_of_url = channel_of_url


    initializer
      getchannel := self # getchannel

    method private getchannel xid =
      let rel_url = url_of_id xid in    (* may raise Not_competent *)

      try
	(* Now compute the absolute URL: *)
	let abs_url = 
	  if Neturl.url_provides ~scheme:true rel_url then
	    rel_url
	  else
	    Neturl.apply_relative_url base_url rel_url in
            (* may raise Malformed_URL *)

	(* Simple check whether 'abs_url' is really absolute: *)
	if not(Neturl.url_provides ~scheme:true abs_url)
	then raise Not_competent;

	own_url <- abs_url;
        (* FIXME: Copy 'abs_url' ? *)

	(* Get and return the channel: *)
	channel_of_url xid abs_url            (* may raise Not_competent *)
      with
	  Neturl.Malformed_URL -> raise (Not_resolvable Neturl.Malformed_URL)
	| Not_competent        -> raise (Not_resolvable Not_found)

    method clone =
      let c =
	new resolve_read_url_channel
	  ?base_url:(Some own_url)
	  ?close:(Some close)
	  ~url_of_id:url_of_id
	  ~channel_of_url:channel_of_url
	  ()
      in
      c # init_rep_encoding internal_encoding;
      c # init_warner warner;
      clones <- c :: clones;
      (c :> resolve_read_url_channel)
  end
;;


type spec = [ `Not_recognized | `Allowed | `Required ]

class resolve_as_file
  ?(file_prefix = (`Allowed :> spec))
  ?(host_prefix = (`Allowed :> spec))
  ?(system_encoding = `Enc_utf8)
  ?(map_private_id = (fun _ -> raise Not_competent))
  ?(open_private_id = (fun _ -> raise Not_competent))
  ()
  =

  let url_syntax =
    let enable_if =
      function
	  `Not_recognized  -> Neturl.Url_part_not_recognized
	| `Allowed         -> Neturl.Url_part_allowed
	| `Required        -> Neturl.Url_part_required
    in
    { Neturl.null_url_syntax with
	Neturl.url_enable_scheme = enable_if file_prefix;
	Neturl.url_enable_host   = enable_if host_prefix;
	Neturl.url_enable_path   = Neturl.Url_part_required;
	Neturl.url_accepts_8bits = true;
    }
  in

  let base_url_syntax =
    { Neturl.null_url_syntax with
	Neturl.url_enable_scheme = Neturl.Url_part_required;
	Neturl.url_enable_host   = Neturl.Url_part_allowed;
	Neturl.url_enable_path   = Neturl.Url_part_required;
	Neturl.url_accepts_8bits = true;
    }
  in

  let default_base_url =
    let cwd = Sys.getcwd() in
    let cwd_slash = if cwd = "/" then cwd else cwd ^ "/" in
    Neturl.make_url
      ~scheme: "file"
      ~host:   ""
      ~path:   (Neturl.split_path cwd_slash)
      base_url_syntax
  in

  let file_url_of_id xid =
    let file_url_of_sysname sysname =
      (* By convention, we can assume that sysname is a URL conforming
       * to RFC 1738 with the exception that it may contain non-ASCII
       * UTF-8 characters.
       *)
      try
	Neturl.url_of_string url_syntax sysname
          (* may raise Malformed_URL *)
      with
	  Neturl.Malformed_URL -> raise Not_competent
    in
    let url =
      match xid with
	  Anonymous          -> raise Not_competent
	| Public (_,sysname) -> if sysname <> "" then file_url_of_sysname sysname
                                                 else raise Not_competent
	| System sysname     -> file_url_of_sysname sysname
	| Private pid        -> map_private_id pid
    in
    let scheme =
      try Neturl.url_scheme url with Not_found -> "file" in
    let host =
      try Neturl.url_host url with Not_found -> "" in

    if scheme <> "file" then raise Not_competent;
    if host <> "" && host <> "localhost" then raise Not_competent;

    url
  in

  let channel_of_file_url xid url =
    match xid with
	Private pid -> open_private_id pid
      | _ ->
	  ( try
	      let path_utf8 =
		try Neturl.join_path (Neturl.url_path ~encoded:false url)
		with Not_found -> raise Not_competent
	      in
	      
	      let path =
		Netconversion.recode_string
		  ~in_enc:  `Enc_utf8
		  ~out_enc: system_encoding
		  path_utf8 in
              (* May raise Malformed_code *)
	      
	      open_in_bin path, None
		(* May raise Sys_error *)
		
	    with
	      | Netconversion.Malformed_code -> assert false
  	        (* should not happen *)
	      | Sys_error _ as e ->
		  raise (Not_resolvable e)
	  )
  in

  resolve_read_url_channel
    ~base_url:       default_base_url
    ~url_of_id:      file_url_of_id
    ~channel_of_url: channel_of_file_url
    ()
;;


let make_file_url ?(system_encoding = `Enc_utf8) ?(enc = `Enc_utf8) filename =
  let utf8_filename =
    Netconversion.recode_string
    ~in_enc: enc
    ~out_enc: `Enc_utf8 
      filename
  in

  let utf8_abs_filename =
    if utf8_filename <> "" && utf8_filename.[0] = '/' then
      utf8_filename
    else
      let cwd = Sys.getcwd() in
      let cwd_utf8 =
	Netconversion.recode_string
	~in_enc: system_encoding
	~out_enc: `Enc_utf8 cwd in
      if cwd = "/" then "/" ^ utf8_filename else cwd_utf8 ^ "/" ^ utf8_filename
  in

  let syntax = { Neturl.ip_url_syntax with Neturl.url_accepts_8bits = true } in
  let url = Neturl.make_url
	    ~scheme:"file"
	    ~host:"localhost"
	    ~path:(Neturl.split_path utf8_abs_filename)
	      syntax
  in
  url
;;


class lookup_public_id (catalog : (string * resolver) list) =
  let norm_catalog =
    List.map (fun (id,s) -> Pxp_aux.normalize_public_id id, s) catalog in
( object (self)
    val cat = norm_catalog
    val mutable internal_encoding = `Enc_utf8
    val mutable warner = new drop_warnings
    val mutable active_resolver = None

    method init_rep_encoding enc =
      internal_encoding <- enc

    method init_warner w =
      warner <- w;

    method rep_encoding = internal_encoding
      (* CAUTION: This may not be the truth! *)

    method open_in xid =

      if active_resolver <> None then failwith "Pxp_reader.lookup_* # open_in";

      let r =
	match xid with
	    Public(pubid,_) ->
	      begin
		(* Search pubid in catalog: *)
		try
		  let norm_pubid = Pxp_aux.normalize_public_id pubid in
		  List.assoc norm_pubid cat
		with
		    Not_found ->
		      raise Not_competent
	      end
	  | _ ->
	      raise Not_competent
      in

      let r' = r # clone in
      r' # init_rep_encoding internal_encoding;
      r' # init_warner warner;
      let lb = r' # open_in xid in   (* may raise Not_competent *)
      active_resolver <- Some r';
      lb

    method close_in =
      match active_resolver with
	  None   -> ()
	| Some r -> r # close_in;
	            active_resolver <- None

    method close_all =
      self # close_in

    method change_encoding (enc:string) =
      match active_resolver with
	  None   -> failwith "Pxp_reader.lookup_* # change_encoding"
	| Some r -> r # change_encoding enc

    method clone =
      let c = new lookup_public_id cat in
      c # init_rep_encoding internal_encoding;
      c # init_warner warner;
      c
  end : resolver )
;;


let lookup_public_id_as_file ?(fixenc:encoding option) catalog =
  let ch_of_id filename id =
    let ch = open_in_bin filename in  (* may raise Sys_error *)
    ch, fixenc
  in
  let catalog' =
    List.map
      (fun (id,s) ->
	 (id, new resolve_read_any_channel (ch_of_id s) ())
      )
      catalog
  in
  new lookup_public_id catalog'
;;


let lookup_public_id_as_string ?(fixenc:encoding option) catalog =
   let catalog' =
    List.map
      (fun (id,s) ->
	 (id, new resolve_read_any_string (fun _ -> s, fixenc) ())
      )
      catalog
  in
  new lookup_public_id catalog'
;;
   

class lookup_system_id (catalog : (string * resolver) list) =
( object (self)
    val cat = catalog
    val mutable internal_encoding = `Enc_utf8
    val mutable warner = new drop_warnings
    val mutable active_resolver = None

    method init_rep_encoding enc =
      internal_encoding <- enc

    method init_warner w =
      warner <- w;

    method rep_encoding = internal_encoding
      (* CAUTION: This may not be the truth! *)


    method open_in xid =

      if active_resolver <> None then failwith "Pxp_reader.lookup_system_id # open_in";

      let lookup sysid =
	try
	  List.assoc sysid cat
	with
	    Not_found ->
	      raise Not_competent
      in

      let r =
	match xid with
	    System sysid    -> lookup sysid
	  | Public(_,sysid) -> lookup sysid
	  | _               -> raise Not_competent
      in

      let r' = r # clone in
      r' # init_rep_encoding internal_encoding;
      r' # init_warner warner;
      let lb = r' # open_in xid in   (* may raise Not_competent *)
      active_resolver <- Some r';
      lb


    method close_in =
      match active_resolver with
	  None   -> ()
	| Some r -> r # close_in;
	            active_resolver <- None

    method close_all =
      self # close_in

    method change_encoding (enc:string) =
      match active_resolver with
	  None   -> failwith "Pxp_reader.lookup_system # change_encoding"
	| Some r -> r # change_encoding enc

    method clone =
      let c = new lookup_system_id cat in
      c # init_rep_encoding internal_encoding;
      c # init_warner warner;
      c
  end : resolver)
;;


let lookup_system_id_as_file ?(fixenc:encoding option) catalog =
  let ch_of_id filename id =
    let ch = open_in_bin filename in  (* may raise Sys_error *)
    ch, fixenc
  in
  let catalog' =
    List.map
      (fun (id,s) ->
	 (id, new resolve_read_any_channel (ch_of_id s) ())
      )
      catalog
  in
  new lookup_system_id catalog'
;;


let lookup_system_id_as_string ?(fixenc:encoding option) catalog =
   let catalog' =
    List.map
      (fun (id,s) ->
	 (id, new resolve_read_any_string (fun _ -> s, fixenc) ())
      )
      catalog
  in
  new lookup_system_id catalog'
;;
   

type combination_mode =
    Public_before_system
  | System_before_public
;;


class combine ?prefer ?(mode = Public_before_system) rl =
  object (self)
    val prefered_resolver = prefer
    val mode = mode
    val resolvers = (rl : resolver list)
    val mutable internal_encoding = `Enc_utf8
    val mutable warner = new drop_warnings
    val mutable active_resolver = None
    val mutable clones = []

    method init_rep_encoding enc =
      List.iter
	(fun r -> r # init_rep_encoding enc)
	rl;
      internal_encoding <- enc

    method init_warner w =
      List.iter
	(fun r -> r # init_warner w)
	rl;
      warner <- w;

    method rep_encoding = internal_encoding
      (* CAUTION: This may not be the truth! *)

    method open_in xid =
      let rec find_competent_resolver_for xid' rl =
	match rl with
	    r :: rl' ->
	      begin try
		r, (r # open_in xid')
	      with
		  Not_competent -> find_competent_resolver_for xid' rl'
	      end;
	  | [] ->
	      raise Not_competent
      in

      let find_competent_resolver rl =
	match xid with
	    Public(pubid,sysid) ->
	      ( match mode with
		    Public_before_system ->
		      ( try
			  find_competent_resolver_for(Public(pubid,"")) rl
			with
			    Not_competent ->
			      find_competent_resolver_for(System sysid) rl
		      )
		  | System_before_public ->
		      ( try
			  find_competent_resolver_for(System sysid) rl
			with
			    Not_competent ->
			      find_competent_resolver_for(Public(pubid,"")) rl
		      )
	      )
	  | other ->
	      find_competent_resolver_for other rl
      in

      if active_resolver <> None then failwith "Pxp_reader.combine # open_in";
      let r, lb =
	match prefered_resolver with
	    None ->   find_competent_resolver resolvers
	  | Some r -> find_competent_resolver (r :: resolvers)
      in
      active_resolver <- Some r;
      lb

    method close_in =
      match active_resolver with
	  None   -> ()
	| Some r -> r # close_in;
	            active_resolver <- None

    method close_all =
      List.iter (fun r -> r # close_in) clones

    method change_encoding (enc:string) =
      match active_resolver with
	  None   -> failwith "Pxp_reader.combine # change_encoding"
	| Some r -> r # change_encoding enc

    method clone =
      let c =
	match active_resolver with
	    None   ->
	      new combine ?prefer:None ?mode:(Some mode) 
                          (List.map (fun q -> q # clone) resolvers)
	  | Some r ->
	      let r' = r # clone in
	      new combine
		?prefer:(Some r')
		?mode:(Some mode)
		(List.map
		   (fun q -> if q == r then r' else q # clone)
		   resolvers)
      in
      c # init_rep_encoding internal_encoding;
      c # init_warner warner;
      clones <- c :: clones;
      c
  end



(* ======================================================================
 * History:
 *
 * $Log: pxp_reader.ml,v $
 * Revision 1.20  2002/03/08 14:37:15  gerd
 * 	Fixed the case that cwd=/
 *
 * Revision 1.19  2002/02/20 00:25:23  gerd
 * 	using Pxp_lexing instead of Lexing.
 *
 * Revision 1.18  2002/02/18 00:25:40  gerd
 * 	Minor enhancement: String.blit only called for n>0
 *
 * Revision 1.17  2001/10/12 21:38:14  gerd
 * 	Changes for O'caml 3.03-alpha.
 *
 * Revision 1.16  2001/07/01 09:46:40  gerd
 * 	Fix: resolve_read_url_channel does not use the base_url if
 * the current URL is already absolute
 *
 * Revision 1.15  2001/07/01 08:35:23  gerd
 * 	Instead of the ~auto_close argument, there is now a
 * ~close argument for several functions/classes. This allows some
 * additional action when the resolver is closed.
 *
 * Revision 1.14  2001/06/14 23:28:02  gerd
 * 	Fix: class combine works now with private IDs.
 *
 * Revision 1.13  2001/04/22 14:16:48  gerd
 * 	resolve_as_file: you can map private IDs to arbitrary channels.
 * 	resolve_read_url_channel: changed type of the channel_of_url
 * argument (ext_id is also passed)
 * 	More examples and documentation.
 *
 * Revision 1.12  2001/04/21 17:40:48  gerd
 * 	Bugfix in 'combine'
 *
 * Revision 1.11  2001/04/03 20:22:44  gerd
 * 	New resolvers for catalogs of PUBLIC and SYSTEM IDs.
 * 	Improved "combine": PUBLIC and SYSTEM IDs are handled
 * separately.
 * 	Rewritten from_file: Is now a simple application of the
 * Pxp_reader classes and functions. (The same has still to be done
 * for from_channel!)
 *
 * Revision 1.10  2001/02/01 20:38:49  gerd
 * 	New support for PUBLIC identifiers.
 *
 * Revision 1.9  2000/08/14 22:24:55  gerd
 * 	Moved the module Pxp_encoding to the netstring package under
 * the new name Netconversion.
 *
 * Revision 1.8  2000/07/16 18:31:09  gerd
 * 	The exception Illegal_character has been dropped.
 *
 * Revision 1.7  2000/07/09 15:32:01  gerd
 * 	Fix in resolve_this_channel, resolve_this_string
 *
 * Revision 1.6  2000/07/09 01:05:33  gerd
 * 	New methode 'close_all' that closes the clones, too.
 *
 * Revision 1.5  2000/07/08 16:24:56  gerd
 * 	Introduced the exception 'Not_resolvable' to indicate that
 * 'combine' should not try the next resolver of the list.
 *
 * Revision 1.4  2000/07/06 23:04:46  gerd
 * 	Quick fix for 'combine': The active resolver is "prefered",
 * but the other resolvers are also used.
 *
 * Revision 1.3  2000/07/06 21:43:45  gerd
 * 	Fix: Public(_,name) is now treated as System(name) if
 * name is non-empty.
 *
 * Revision 1.2  2000/07/04 22:13:30  gerd
 * 	Implemented the new API rev. 1.2 of pxp_reader.mli.
 *
 * Revision 1.1  2000/05/29 23:48:38  gerd
 * 	Changed module names:
 * 		Markup_aux          into Pxp_aux
 * 		Markup_codewriter   into Pxp_codewriter
 * 		Markup_document     into Pxp_document
 * 		Markup_dtd          into Pxp_dtd
 * 		Markup_entity       into Pxp_entity
 * 		Markup_lexer_types  into Pxp_lexer_types
 * 		Markup_reader       into Pxp_reader
 * 		Markup_types        into Pxp_types
 * 		Markup_yacc         into Pxp_yacc
 * See directory "compatibility" for (almost) compatible wrappers emulating
 * Markup_document, Markup_dtd, Markup_reader, Markup_types, and Markup_yacc.
 *
 * ======================================================================
 * Old logs from markup_reader.ml:
 *
 * Revision 1.3  2000/05/29 21:14:57  gerd
 * 	Changed the type 'encoding' into a polymorphic variant.
 *
 * Revision 1.2  2000/05/20 20:31:40  gerd
 * 	Big change: Added support for various encodings of the
 * internal representation.
 *
 * Revision 1.1  2000/03/13 23:41:44  gerd
 * 	Initial revision; this code was formerly part of Markup_entity.
 *
 *
 *)

This web site is published by Informatikbüro Gerd Stolpmann
Powered by Caml