Repository: xonsh/xonsh

Issue:
coreutils cat can't read /proc/*
Hey ! cat from coreutils doesn't return anything when reading files from `/proc` :
```
$ cat /proc/cpuinfo                                                                                                                                                                            
$ /usr/bin/cat /proc/cpuinfo                                                                                                                                                                   
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 60
model name      : Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz
```

Here is my config :
```
+------------------+---------------------+
| xonsh            | 0.9.6               |
| Git SHA          | dabd4bd3            |
| Commit Date      | Jun 6 09:44:17 2019 |
| Python           | 3.7.3               |
| PLY              | 3.11                |
| have readline    | True                |
| prompt toolkit   | 2.0.9               |
| shell type       | prompt_toolkit2     |
| pygments         | 2.4.2               |
| on posix         | True                |
| on linux         | True                |
| distro           | unknown             |
| on darwin        | False               |
| on windows       | False               |
| on cygwin        | False               |
| on msys2         | False               |
| is superuser     | False               |
| default encoding | utf-8               |
| xonsh encoding   | utf-8               |
| encoding errors  | surrogateescape     |
+------------------+---------------------+
```

Discussion:

Patch:
diff --git a/news/3182-fix-cat-proc-file.rst b/news/3182-fix-cat-proc-file.rst
new file mode 100644
index 00000000..c26c5882
--- /dev/null
+++ b/news/3182-fix-cat-proc-file.rst
@@ -0,0 +1,23 @@
+**Added:**
+
+* <news item>
+
+**Changed:**
+
+* <news item>
+
+**Deprecated:**
+
+* <news item>
+
+**Removed:**
+
+* <news item>
+
+**Fixed:**
+
+* Fix ``cat`` can't read pseudo files with zero size such as /proc/\* or /sys/\* (#3182, #3199)
+
+**Security:**
+
+* <news item>
diff --git a/xonsh/xoreutils/cat.py b/xonsh/xoreutils/cat.py
index 2a89f141..73f172bf 100644
--- a/xonsh/xoreutils/cat.py
+++ b/xonsh/xoreutils/cat.py
@@ -1,6 +1,5 @@
 """Implements a cat command for xonsh."""
 import os
-import stat
 import time
 import builtins
 
@@ -12,15 +11,17 @@ def _cat_line(
     f, sep, last_was_blank, line_count, opts, out, enc, enc_errors, read_size
 ):
     _r = r = f.readline(size=80)
+    restore_newline = False
     if isinstance(_r, str):
         _r = r = _r.encode(enc, enc_errors)
     if r == b"":
-        last_was_blank, line_count, read_size, True
+        return last_was_blank, line_count, read_size, True
     if r.endswith(sep):
         _r = _r[: -len(sep)]
+        restore_newline = True
     this_one_blank = _r == b""
     if last_was_blank and this_one_blank and opts["squeeze_blank"]:
-        last_was_blank, line_count, read_size, False
+        return last_was_blank, line_count, read_size, False
     last_was_blank = this_one_blank
     if opts["number_all"] or (opts["number_nonblank"] and not this_one_blank):
         start = ("%6d " % line_count).encode(enc, enc_errors)
@@ -28,6 +29,8 @@ def _cat_line(
         line_count += 1
     if opts["show_ends"]:
         _r = _r + b"$"
+    if restore_newline:
+        _r = _r + sep
     out.buffer.write(_r)
     out.flush()
     read_size += len(r)
@@ -49,9 +52,8 @@ def _cat_single_file(opts, fname, stdin, out, err, line_count=1):
         print("cat: No such file or directory: {}".format(fname), file=err)
         return True, line_count
     else:
-        fstat = os.stat(fname)
-        file_size = fstat.st_size
-        if file_size == 0 and not stat.S_ISREG(fstat.st_mode):
+        file_size = os.stat(fname).st_size
+        if file_size == 0:
             file_size = None
         fobj = open(fname, "rb")
         f = xproc.NonBlockingFDReader(fobj.fileno(), timeout=0.1)


Test patch:
diff --git a/tests/test_xoreutils.py b/tests/test_xoreutils.py
index 34642e20..2253b77b 100644
--- a/tests/test_xoreutils.py
+++ b/tests/test_xoreutils.py
@@ -191,6 +191,24 @@ class TestCat:
         assert stdout_buf.getvalue() == bytes(expected_content, "utf-8")
         assert stderr_buf.getvalue() == b''
 
+    def test_cat_single_file_with_end_newline(self, cat_env_fixture):
+        content = "this is a content withe \\n\nfor testing xoreutil's cat\n"
+        with open(self.tempfile, "w") as f:
+            f.write(content)
+        expected_content = content.replace("\n", os.linesep)
+
+        stdin = io.StringIO()
+        stdout_buf = io.BytesIO()
+        stderr_buf = io.BytesIO()
+        stdout = io.TextIOWrapper(stdout_buf)
+        stderr = io.TextIOWrapper(stderr_buf)
+        opts = cat._cat_parse_args([])
+        cat._cat_single_file(opts, self.tempfile, stdin, stdout, stderr)
+        stdout.flush()
+        stderr.flush()
+        assert stdout_buf.getvalue() == bytes(expected_content, "utf-8")
+        assert stderr_buf.getvalue() == b''
+
     def test_cat_empty_file(self, cat_env_fixture):
         with open(self.tempfile, "w") as f:
             f.write("")