diff --git a/CHANGELOG b/CHANGELOG index ef4c9526..8657bd2d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,11 @@ jc changelog +20210813 v1.16.1 +- Note to Package Maintainers: please see note at 20210720 v1.16.0 +- Fix issue with process substitution with the magic syntax +- Fix issue with globs not including filenames with spaces with magic syntax +- Fix stat parser to properly handle filenames with spaces on macOS/BSD + 20210720 v1.16.0 - Note to Package Maintainers: TL;DR: `/man/jc.1.gz` and `/jc/man/jc.1.gz` are deprecated and only `/man/jc.1` should be used. diff --git a/docs/parsers/stat.md b/docs/parsers/stat.md index 350b25f5..3e62440b 100644 --- a/docs/parsers/stat.md +++ b/docs/parsers/stat.md @@ -193,4 +193,4 @@ Returns: ## Parser Information Compatibility: linux, darwin, freebsd -Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com) +Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com) diff --git a/jc/__init__.py b/jc/__init__.py index a1f56da0..07cd69d4 100644 --- a/jc/__init__.py +++ b/jc/__init__.py @@ -86,4 +86,4 @@ Module Example: """ name = 'jc' -__version__ = '1.16.0' +__version__ = '1.16.1' diff --git a/jc/cli.py b/jc/cli.py index 4dafde69..c4efee46 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -6,10 +6,10 @@ import sys import os import os.path import re -import shlex import importlib import textwrap import signal +import shlex import subprocess import json import jc @@ -360,11 +360,10 @@ def help_doc(options): # load parser module just in time so we don't need to load all modules parser = parser_module(arg) compatible = ', '.join(parser.info.compatible) - doc_text = f'''{parser.__doc__} -Compatibility: {compatible} - -Version {parser.info.version} by {parser.info.author} ({parser.info.author_email}) -''' + doc_text = \ + f'{parser.__doc__}\n'\ + f'Compatibility: {compatible}\n\n'\ + f'Version {parser.info.version} by {parser.info.author} ({parser.info.author_email})\n' return doc_text @@ -415,8 +414,7 @@ def magic_parser(args): if len(args) <= 1 or args[1].startswith('--'): return False, None, None, [] - # correctly parse escape characters and spaces with shlex - args_given = ' '.join(map(shlex.quote, args[1:])).split() + args_given = args[1:] options = [] # find the options @@ -466,7 +464,11 @@ def magic_parser(args): def run_user_command(command): """Use subprocess to run the user's command. Returns the STDOUT, STDERR, and the Exit Code as a tuple.""" - proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + proc = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + close_fds=False, # Allows inheriting file descriptors. Useful for process substitution + universal_newlines=True) stdout, stderr = proc.communicate() return ( @@ -545,7 +547,10 @@ def main(): # if magic syntax used, try to run the command and error if it's not found, etc. magic_stdout, magic_stderr, magic_exit_code = None, None, 0 if run_command: - run_command_str = ' '.join(run_command) + try: + run_command_str = shlex.join(run_command) # python 3.8+ + except AttributeError: + run_command_str = ' '.join(run_command) # older python versions if valid_command: try: @@ -560,6 +565,13 @@ def main(): jc.utils.error_message(f'"{run_command_str}" command could not be found. For details use the -d or -dd option.') sys.exit(combined_exit_code(magic_exit_code, JC_ERROR_EXIT)) + except OSError: + if debug: + raise + else: + jc.utils.error_message(f'"{run_command_str}" command could not be run due to too many open files. For details use the -d or -dd option.') + sys.exit(combined_exit_code(magic_exit_code, JC_ERROR_EXIT)) + except Exception: if debug: raise diff --git a/jc/man/jc.1.gz b/jc/man/jc.1.gz index c113d79e..c857ff2b 100644 Binary files a/jc/man/jc.1.gz and b/jc/man/jc.1.gz differ diff --git a/jc/parsers/stat.py b/jc/parsers/stat.py index d071bf31..6ed8e6cc 100644 --- a/jc/parsers/stat.py +++ b/jc/parsers/stat.py @@ -169,7 +169,7 @@ import jc.utils class info(): """Provides parser metadata (version, author, etc.)""" - version = '1.8' + version = '1.9' description = '`stat` command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' @@ -322,7 +322,7 @@ def parse(data, raw=False, quiet=False): for line in cleandata: value = shlex.split(line) output_line = { - 'file': value[15], + 'file': ' '.join(value[15:]), 'unix_device': value[0], 'inode': value[1], 'flags': value[2], diff --git a/man/jc.1 b/man/jc.1 index 9145267a..521de35d 100644 --- a/man/jc.1 +++ b/man/jc.1 @@ -1,4 +1,8 @@ +<<<<<<< HEAD +.TH jc 1 2021-08-13 1.16.1 "JSON CLI output utility" +======= .TH jc 1 2021-07-22 1.16.0 "JSON CLI output utility" +>>>>>>> master .SH NAME jc \- JSONifies the output of many CLI tools and file-types .SH SYNOPSIS diff --git a/man/jc.1.gz b/man/jc.1.gz index c113d79e..c857ff2b 100644 Binary files a/man/jc.1.gz and b/man/jc.1.gz differ diff --git a/setup.py b/setup.py index 02c3b6bc..4bded3c1 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open('README.md', 'r') as f: setuptools.setup( name='jc', - version='1.16.0', + version='1.16.1', author='Kelly Brazil', author_email='kellyjonbrazil@gmail.com', description='Converts the output of popular command-line tools and file-types to JSON.', diff --git a/tests/fixtures/osx-10.14.6/stat-filename-with-spaces.json b/tests/fixtures/osx-10.14.6/stat-filename-with-spaces.json new file mode 100644 index 00000000..5b85c4f7 --- /dev/null +++ b/tests/fixtures/osx-10.14.6/stat-filename-with-spaces.json @@ -0,0 +1 @@ +[{"file":"file name with spaces.txt","unix_device":16777220,"inode":161929661,"flags":"-rw-r--r--","links":1,"user":"kbrazil","group":"staff","rdev":0,"size":0,"access_time":"Aug 13 15:03:52 2021","modify_time":"Aug 13 14:37:03 2021","change_time":"Aug 13 14:37:03 2021","birth_time":"Aug 13 14:37:03 2021","block_size":4096,"blocks":0,"unix_flags":"0","access_time_epoch":1628892232,"access_time_epoch_utc":null,"modify_time_epoch":1628890623,"modify_time_epoch_utc":null,"change_time_epoch":1628890623,"change_time_epoch_utc":null,"birth_time_epoch":1628890623,"birth_time_epoch_utc":null}] diff --git a/tests/fixtures/osx-10.14.6/stat-filename-with-spaces.out b/tests/fixtures/osx-10.14.6/stat-filename-with-spaces.out new file mode 100644 index 00000000..083d89fd --- /dev/null +++ b/tests/fixtures/osx-10.14.6/stat-filename-with-spaces.out @@ -0,0 +1,2 @@ +16777220 161929661 -rw-r--r-- 1 kbrazil staff 0 0 "Aug 13 15:03:52 2021" "Aug 13 14:37:03 2021" "Aug 13 14:37:03 2021" "Aug 13 14:37:03 2021" 4096 0 0 file name with spaces.txt + diff --git a/tests/test_stat.py b/tests/test_stat.py index 9d4c84ac..3f8ad60c 100644 --- a/tests/test_stat.py +++ b/tests/test_stat.py @@ -26,6 +26,9 @@ class MyTests(unittest.TestCase): with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/stat.out'), 'r', encoding='utf-8') as f: self.osx_10_14_6_stat = f.read() + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/stat-filename-with-spaces.out'), 'r', encoding='utf-8') as f: + self.osx_10_14_6_stat_filename_with_spaces = f.read() + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/freebsd12/stat.out'), 'r', encoding='utf-8') as f: self.freebsd12_stat = f.read() @@ -39,6 +42,9 @@ class MyTests(unittest.TestCase): with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/stat.json'), 'r', encoding='utf-8') as f: self.osx_10_14_6_stat_json = json.loads(f.read()) + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/stat-filename-with-spaces.json'), 'r', encoding='utf-8') as f: + self.osx_10_14_6_stat_filename_with_spaces_json = json.loads(f.read()) + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/freebsd12/stat.json'), 'r', encoding='utf-8') as f: self.freebsd12_stat_json = json.loads(f.read()) @@ -66,6 +72,12 @@ class MyTests(unittest.TestCase): """ self.assertEqual(jc.parsers.stat.parse(self.osx_10_14_6_stat, quiet=True), self.osx_10_14_6_stat_json) + def test_stat_filename_with_spaces_osx_10_14_6(self): + """ + Test 'stat' filename with spaces on OSX 10.14.6 + """ + self.assertEqual(jc.parsers.stat.parse(self.osx_10_14_6_stat_filename_with_spaces, quiet=True), self.osx_10_14_6_stat_filename_with_spaces_json) + def test_stat_freebsd12(self): """ Test 'stat /foo/*' on FreeBSD12