package body CSV is package SU renames Ada.Strings.Unbounded; function Quoted (Input : in SU.Unbounded_String; Output : out SU.Unbounded_String; Remaining : out SU.Unbounded_String) return Boolean is Result : SU.Unbounded_String; This_In : SU.Unbounded_String; begin if SU.Length (Input) > 0 and then SU.Element (Input, 1) = Quote then Result := SU.To_Unbounded_String (0); This_In := SU.Tail (Input, SU.Length (Input) - 1); while SU.Length (This_In) > 0 and then SU.Element (This_In, 1) /= Quote loop SU.Append (Result, SU.Head (This_In, 1)); This_In := SU.Tail (This_In, SU.Length (This_In) - 1); end loop; Output := Result; if SU.Length (This_In) > 0 then Remaining := SU.Tail (This_In, SU.Length (This_In) - 1); else Remaining := SU.To_Unbounded_String (0); end if; return True; else return False; end if; end Quoted; function Escape_Char (Input : in SU.Unbounded_String; Output : out SU.Unbounded_String; Remaining : out SU.Unbounded_String) return Boolean is begin if SU.Length (Input) > 1 and then SU.Element (Input, 1) = Escape then Output := SU.Unbounded_Slice (Input, 2, 1); Remaining := SU.Tail (Input, SU.Length (Input) - 2); return True; else return False; end if; end Escape_Char; function Field_Char (Input : in SU.Unbounded_String; Output : out SU.Unbounded_String; Remaining : out SU.Unbounded_String) return Boolean is begin if SU.Length (Input) > 0 and then SU.Element (Input, 1) /= Delimiter and then SU.Element (Input, 1) /= Quote and then SU.Element (Input, 1) /= Escape then Output := SU.Head (Input, 1); Remaining := SU.Tail (Input, SU.Length (Input) - 1); return True; else return False; end if; end Field_Char; procedure Field (Input : in SU.Unbounded_String; Output : out SU.Unbounded_String; Remaining : out SU.Unbounded_String) is Result : SU.Unbounded_String; This_In, This_Out, This_Remaining : SU.Unbounded_String; begin Result := SU.To_Unbounded_String (0); This_In := Input; while Field_Char (This_In, This_Out, This_Remaining) or else Escape_Char (This_In, This_Out, This_Remaining) or else Quoted (This_In, This_Out, This_Remaining) loop SU.Append (Result, This_Out); This_In := This_Remaining; end loop; Output := Result; Remaining := This_In; end Field; function Separator (Input : in SU.Unbounded_String; Remaining : out SU.Unbounded_String) return Boolean is begin if SU.Length (Input) > 0 and then SU.Element (Input, 1) = Delimiter then Remaining := SU.Tail (Input, SU.Length (Input) - 1); return True; else return False; end if; end Separator; function Parse_Line (Input : in String) return CSV_Record is Result : CSV_Record := String_Vectors.Empty_Vector; This_In, This_Out, This_Remaining : SU.Unbounded_String; begin This_In := SU.To_Unbounded_String (Input); loop Field (This_In, This_Out, This_Remaining); Result.Append (This_Out); exit when not Separator (This_Remaining, This_In); end loop; return Result; end Parse_Line; function Encode_String (Input : in SU.Unbounded_String) return SU.Unbounded_String is Result : SU.Unbounded_String; Elem : Character; Needs_Quotes : Boolean := False; begin Result := SU.To_Unbounded_String (0); for I in Integer range 1 .. SU.Length (Input) loop Elem := SU.Element (Input, I); if Elem = Quote or Elem = Escape then SU.Append (Result, Escape & Elem); else SU.Append (Result, Elem); end if; if Elem = Delimiter then Needs_Quotes := True; end if; end loop; if Needs_Quotes then Result := Quote & Result & Quote; end if; return Result; end Encode_String; function Unparse_Record (Input : in CSV_Record) return String is Result : SU.Unbounded_String; begin Result := SU.To_Unbounded_String (0); for Item of Input loop SU.Append (Result, Encode_String (Item) & Delimiter); end loop; return SU.Slice (Result, 1, SU.Length (Result) - 1); end Unparse_Record; end CSV;