----------------------------------------------------------------------------- -- -- Onions Network Streams Library -- -- O N I O N S . I N S T R E A M S . L I N E B U F -- -- B o d y -- -- Copyright (C) 1997-1998 Regents of the University of California -- -- Onions is free software; you can redistribute it and/or modify it under -- the terms of the GNU General Public License as published by the Free -- Software Foundation, with or without the single exception listed below; -- either version 2, or (at your option) any later version. Onions is -- distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -- without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -- PARTICULAR PURPOSE. See the GNU General Public License for more details. -- You should have received a copy of the GNU General Public License -- distributed with Onions; see the file COPYING. If not, write to the -- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA -- 02111-1307, USA. -- -- As a special exception, if other files instantiate generics from this -- library, or you link this library with other files to produce an -- executable, this library does not by itself cause the resulting -- executable to be covered by the GNU General Public License. This -- exception does not however invalidate any other reasons why the -- executable file might be covered by the GNU General Public License. -- -- Created in 1997 by Roy T. Fielding ----------------------------------------------------------------------------- -- -- The Line Buffered Input Stream is a filter that coalesces input data -- into a complete line terminated by LF. CRLF or bare CR is translated -- to LF, but that's the only magic we do. -- with Ada.Characters.Latin_1; with Ada.Streams; use Ada.Streams; with Onions.Buckets; use Onions.Buckets; with Unchecked_Deallocation; package body Onions.Instreams.Linebuf is use Bucket_Queues; --------------------------------------------- -- Dispatching Stream Control Operations -- --------------------------------------------- procedure Dispose is new Unchecked_Deallocation (LB_Input_Stream, LB_Input_Stream_Ptr); -- Free the storage associated with a stream object. -- procedure Free (SP : in out LB_Input_Stream_Ptr) is ItemList : Bucket_List; begin if SP /= null then if Length (SP.Unprocessed) > 0 then Dequeue (SP.Unprocessed, ItemList); Free (ItemList); end if; if Length (SP.Processed) > 0 then Dequeue (SP.Processed, ItemList); Free (ItemList); end if; if not IsEmpty (SP.DoneList) then Free (SP.DoneList); end if; Dispose (SP); end if; end Free; -- Close a stream object and propagate the close upstream. -- procedure Close (Stream : in out LB_Input_Stream) is begin Close (Stream.Outbound); end Close; -- Abort_Stream should only be used if a stream is interrupted -- by the user, or an error occurs that makes the whole stream bad. -- It forces the stream closed without a flush. -- procedure Abort_Stream (Stream : in out LB_Input_Stream) is ItemList : Bucket_List; begin if Length (Stream.Unprocessed) > 0 then Dequeue (Stream.Unprocessed, ItemList); Free (ItemList); end if; if Length (Stream.Processed) > 0 then Dequeue (Stream.Processed, ItemList); Free (ItemList); end if; if not IsEmpty (Stream.DoneList) then Free (Stream.DoneList); end if; Abort_Stream (Stream.Outbound); end Abort_Stream; -- Reset is like Close, but resets the stream to the state -- it would be in if it was just created. It discards -- anything in its own buffers. -- procedure Reset (Stream : in out LB_Input_Stream) is ItemList : Bucket_List; begin if Length (Stream.Unprocessed) > 0 then Dequeue (Stream.Unprocessed, ItemList); Free (ItemList); end if; if Length (Stream.Processed) > 0 then Dequeue (Stream.Processed, ItemList); Free (ItemList); end if; if not IsEmpty (Stream.DoneList) then Free (Stream.DoneList); end if; Stream.Byte_Count := 0; Stream.Timeout := 0; Stream.System_Error := 0; if Stream.Outbound /= null then Reset (Stream.Outbound); end if; end Reset; -- Drain the stream by reading once from outbound and processing any -- unprocessed data as if it were the end-of-stream. -- procedure Drain (Stream : in out LB_Input_Stream; ItemList : out Bucket_List) is begin if Stream.Outbound /= null then Drain (Stream.Outbound, ItemList); Enqueue (Stream.Unprocessed, ItemList); end if; Process (Stream, True); Dequeue (Stream.Processed, ItemList); end Drain; ------------------------------------ -- Ada.Streams Dispatching Read -- ------------------------------------ -- Read obtains stream elements from Stream and places them into -- the components of Item until each component is filled or -- no elements remain on the stream or a complete line has been read. -- Last is set to the index of the last filled component of Item. -- This interface is defined by Ada.Streams for abstract stream -- operations. We won't use it much because it forces a full data copy -- when filtering. -- procedure Read (Stream : in out LB_Input_Stream; Item : out Stream_Element_Array; Last : out Stream_Element_Offset) is ReadList : Bucket_List; begin Last := Item'First - 1; while Length (Stream.Processed) = 0 loop Process (Stream, False); end loop; Dequeue (Stream.Processed, ReadList); Dump_Into (ReadList, Item, Last); if not IsEmpty (ReadList) then Undequeue (Stream.Processed, ReadList); end if; exception when End_Error => if Last < Item'First then raise End_Error; end if; end Read; ---------------------------------- -- Data Processing Operations -- ---------------------------------- -- Process does the magic necessary to read from the upstream object -- and move the data from the Unprocessed read queue to the Processed -- read queue. In this case, it just waits for a full line of data and -- coalesces the line into a single bucket. If Everything, then process -- the entire Unprocessed buffer as if it were the end-of-stream. -- Raises End_Error if end-of-stream is encountered and nothing has been -- processed, or Status_Error if the outbound stream is not connected. -- procedure Process (Stream : in out LB_Input_Stream; Everything : Boolean) is ItemList : Bucket_List; Bucko : Bucket; EOS : Boolean := Everything; Complete_Line : Boolean; begin if not EOS then if Stream.Outbound /= null then begin Read (Stream.Outbound, ItemList); -- Only the outermost object should update Byte_Count here. Enqueue (Stream.Unprocessed, ItemList); exception when End_Error => if Length (Stream.Unprocessed) = 0 then raise End_Error; else EOS := True; end if; end; else raise Status_Error; end if; end if; if Length (Stream.Unprocessed) > 0 then Dequeue (Stream.Unprocessed, ItemList); -- This is where ItemList would be filtered (entirely if EOS) -- We are simply coalescing buckets into complete line buckets. -- loop Dump_Line (ItemList, Stream.DoneList, Complete_Line); if Complete_Line then Combine (Stream.DoneList, Bucko); Enqueue (Stream.Processed, Bucko); else if EOS then if not IsEmpty (Stream.DoneList) then Combine (Stream.DoneList, Bucko); Enqueue (Stream.Processed, Bucko); end if; Free (ItemList); end if; exit; end if; if IsEmpty (ItemList) then return; end if; end loop; if not IsEmpty (ItemList) then Undequeue (Stream.Unprocessed, ItemList); end if; end if; end Process; -- We don't need to override Unprocess since this filter doesn't -- change the content of the data, aside from CRLF --> LF and we -- can't keep track of that anyway. end Onions.Instreams.Linebuf;