Skip to content

Commit 7c3abef

Browse files
committed
Fix issues with handling of Universal_Newline filtering
Implementation was wrong whenever captured output was not buffered and read in one chunk. Part V114-021 Change-Id: I22766ebc37b3f0fee6bca69b3e01d205f847461e
1 parent 101a368 commit 7c3abef

File tree

3 files changed

+115
-10
lines changed

3 files changed

+115
-10
lines changed

src/os/gnatcoll-os-process.adb

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
------------------------------------------------------------------------------
22
-- G N A T C O L L --
33
-- --
4-
-- Copyright (C) 2021, AdaCore --
4+
-- Copyright (C) 2021-2022, AdaCore --
55
-- --
66
-- This library is free software; you can redistribute it and/or modify it --
77
-- under terms of the GNU General Public License as published by the Free --
@@ -256,18 +256,40 @@ package body GNATCOLL.OS.Process is
256256
-- Read output and filter out if necessary CR characters. Note that
257257
-- call to Read is blocking.
258258
if Universal_Newline then
259-
loop
260-
N := FS.Read (Pipe_Read, Buffer);
261-
exit when N <= 0;
259+
declare
260+
Prev_Chunk_Last_Is_CR : Boolean := False;
261+
begin
262+
loop
263+
N := FS.Read (Pipe_Read, Buffer);
264+
exit when N <= 0;
265+
266+
-- Check if last chunk ended with a CR and first char in this
267+
-- chunk is a LF
268+
if Prev_Chunk_Last_Is_CR and then Buffer (1) /= ASCII.LF then
269+
Append (Output, ASCII.CR);
270+
end if;
262271

263-
for Index in 1 .. N - 1 loop
264-
if not (Buffer (Index) = ASCII.CR
265-
and then Buffer (Index + 1) = ASCII.LF)
266-
then
267-
Append (Output, Buffer (Index));
272+
for Index in 1 .. N - 1 loop
273+
if Buffer (Index) /= ASCII.CR
274+
or else Buffer (Index + 1) /= ASCII.LF
275+
then
276+
Append (Output, Buffer (Index));
277+
end if;
278+
end loop;
279+
280+
if Buffer (N) = ASCII.CR then
281+
Prev_Chunk_Last_Is_CR := True;
282+
else
283+
Prev_Chunk_Last_Is_CR := False;
284+
Append (Output, Buffer (N));
268285
end if;
269286
end loop;
270-
end loop;
287+
288+
-- Trailing CR
289+
if Prev_Chunk_Last_Is_CR then
290+
Append (Output, ASCII.CR);
291+
end if;
292+
end;
271293
else
272294
loop
273295
N := FS.Read (Pipe_Read, Buffer);
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
with GNATCOLL.OS.Process;
2+
with GNATCOLL.OS.Process_Types;
3+
with GNATCOLL.OS.FS;
4+
with GNATCOLL.OS;
5+
with GNATCOLL.OS.Constants;
6+
with Test_Assert;
7+
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
8+
with Ada.Command_Line;
9+
with Ada.Text_IO;
10+
11+
function Test return Integer is
12+
package A renames Test_Assert;
13+
package IO renames Ada.Text_IO;
14+
package OS renames GNATCOLL.OS;
15+
package PT renames GNATCOLL.OS.Process_Types;
16+
package FS renames GNATCOLL.OS.FS;
17+
18+
use type GNATCOLL.OS.OS_Type;
19+
20+
Msg : constant String := "a" & ASCII.CR & ASCII.LF & "b" & ASCII.CR & "c";
21+
22+
Non_Filtered_Out : constant String := (
23+
if OS.Constants.OS = OS.Windows then
24+
"a" & ASCII.CR & ASCII.CR & ASCII.LF & "b" & ASCII.CR & "c"
25+
else
26+
"a" & ASCII.CR & ASCII.LF & "b" & ASCII.CR & "c");
27+
28+
Filtered_Out : constant String := (
29+
if OS.Constants.OS = OS.Windows then
30+
"a" & ASCII.CR & ASCII.LF & "b" & ASCII.CR & "c"
31+
else
32+
"a" & ASCII.LF & "b" & ASCII.CR & "c");
33+
34+
procedure Test_IO (Mode : String);
35+
36+
procedure Test_IO (Mode : String) is
37+
Args : PT.Arguments;
38+
Env : PT.Environ;
39+
Status : Integer;
40+
Output : Unbounded_String;
41+
begin
42+
IO.Put_Line ("== Test buffered IO mode=" & Mode & " ==");
43+
PT.Add_Argument (Args, Ada.Command_Line.Command_Name);
44+
PT.Inherit (Env);
45+
46+
-- This the topelevel program that check redirections
47+
PT.Add_Argument (Args, Mode);
48+
49+
Output := OS.Process.Run (Args, Env, Status => Status);
50+
A.Assert (Status, 0, Msg => "program should return 0 as status");
51+
A.Assert (To_String (Output), Non_Filtered_Out, "check_output un=False");
52+
53+
Output := OS.Process.Run
54+
(Args, Env, Status => Status, Universal_Newline => True);
55+
A.Assert (Status, 0, Msg => "program should return 0 as status");
56+
A.Assert (To_String (Output), Filtered_Out, "check_output un=True");
57+
end Test_IO;
58+
59+
begin
60+
61+
if Ada.Command_Line.Argument_Count = 0 then
62+
Test_IO ("non_buffered_io");
63+
Test_IO ("buffered_io");
64+
return A.Report;
65+
66+
elsif Ada.Command_Line.Argument (1) = "buffered_io" then
67+
-- Don't use Ada.Text_IO as some trailing LF is always added.
68+
FS.Write (FS.Standout, Msg);
69+
70+
elsif Ada.Command_Line.Argument (1) = "non_buffered_io" then
71+
-- Emit characters one by one and put delays between them. In
72+
-- OS.Process this ensure we test the case in which not all output is
73+
-- received at once.
74+
for J in Msg'Range loop
75+
FS.Write (FS.Standout, Msg (J) & "");
76+
IO.Flush;
77+
delay 0.05;
78+
end loop;
79+
end if;
80+
81+
return 0;
82+
end Test;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
title: GNATCOLL.OS.Process.Run Universal_Newline filter

0 commit comments

Comments
 (0)