shellscripts 0.23.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shell_scripts/__init__.py +5 -0
- shell_scripts/__main__.py +5 -0
- shell_scripts/commands/__init__.py +81 -0
- shell_scripts/commands/_dc_common.py +138 -0
- shell_scripts/commands/ai_install.py +453 -0
- shell_scripts/commands/bin_links.py +78 -0
- shell_scripts/commands/claude.py +56 -0
- shell_scripts/commands/clean.py +98 -0
- shell_scripts/commands/codex.py +107 -0
- shell_scripts/commands/copilot.py +54 -0
- shell_scripts/commands/dicom2jpg.py +98 -0
- shell_scripts/commands/dicomviewer.py +83 -0
- shell_scripts/commands/diff_cmd.py +50 -0
- shell_scripts/commands/doxygen_cmd.py +159 -0
- shell_scripts/commands/edit_cmd.py +50 -0
- shell_scripts/commands/gemini.py +54 -0
- shell_scripts/commands/kiro.py +54 -0
- shell_scripts/commands/opencode.py +54 -0
- shell_scripts/commands/pdf_crop.py +520 -0
- shell_scripts/commands/pdf_merge.py +180 -0
- shell_scripts/commands/pdf_split_by_format.py +243 -0
- shell_scripts/commands/pdf_split_by_toc.py +197 -0
- shell_scripts/commands/pdf_tiler_090.py +61 -0
- shell_scripts/commands/pdf_tiler_100.py +61 -0
- shell_scripts/commands/pdf_toc_clean.py +175 -0
- shell_scripts/commands/pi.py +61 -0
- shell_scripts/commands/req_cmd.py +362 -0
- shell_scripts/commands/tests_cmd.py +87 -0
- shell_scripts/commands/ubuntu_dark_theme.py +64 -0
- shell_scripts/commands/venv_cmd.py +82 -0
- shell_scripts/commands/video2h264.py +89 -0
- shell_scripts/commands/video2h265.py +87 -0
- shell_scripts/commands/view_cmd.py +50 -0
- shell_scripts/commands/vscode_cmd.py +42 -0
- shell_scripts/commands/vsinsider_cmd.py +42 -0
- shell_scripts/config.py +328 -0
- shell_scripts/core.py +324 -0
- shell_scripts/utils.py +455 -0
- shell_scripts/version_check.py +323 -0
- shellscripts-0.23.1.dist-info/METADATA +310 -0
- shellscripts-0.23.1.dist-info/RECORD +45 -0
- shellscripts-0.23.1.dist-info/WHEEL +5 -0
- shellscripts-0.23.1.dist-info/entry_points.txt +3 -0
- shellscripts-0.23.1.dist-info/licenses/LICENSE +674 -0
- shellscripts-0.23.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from shell_scripts.utils import print_error
|
|
6
|
+
|
|
7
|
+
PROGRAM = "shellscripts"
|
|
8
|
+
DESCRIPTION = "Create or update command symlinks in $HOME/bin."
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def print_help(version):
|
|
12
|
+
print(f"Usage: {PROGRAM} bin-links [options] ({version})")
|
|
13
|
+
print()
|
|
14
|
+
print("bin-links options:")
|
|
15
|
+
print(
|
|
16
|
+
" <directory> - Source directory containing files to link (default: current dir)."
|
|
17
|
+
)
|
|
18
|
+
print(" --dest <dir> - Destination directory for symlinks (default: $HOME/bin).")
|
|
19
|
+
print(" --help - Show this help message.")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def run(args):
|
|
23
|
+
src_dir = Path.cwd()
|
|
24
|
+
dest_dir = Path.home() / "bin"
|
|
25
|
+
|
|
26
|
+
i = 0
|
|
27
|
+
while i < len(args):
|
|
28
|
+
if args[i] == "--dest":
|
|
29
|
+
if i + 1 >= len(args):
|
|
30
|
+
print_error("--dest requires a directory path.")
|
|
31
|
+
return 1
|
|
32
|
+
dest_dir = Path(args[i + 1])
|
|
33
|
+
i += 2
|
|
34
|
+
elif not args[i].startswith("-"):
|
|
35
|
+
src_dir = Path(args[i])
|
|
36
|
+
i += 1
|
|
37
|
+
else:
|
|
38
|
+
print_error(f"Unknown option: {args[i]}")
|
|
39
|
+
return 1
|
|
40
|
+
|
|
41
|
+
if not src_dir.is_dir():
|
|
42
|
+
print_error(f"Source directory not found: {src_dir}")
|
|
43
|
+
return 1
|
|
44
|
+
|
|
45
|
+
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
|
|
47
|
+
for file_path in sorted(src_dir.iterdir()):
|
|
48
|
+
if file_path.is_dir():
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
filename = file_path.name
|
|
52
|
+
abs_src = file_path.resolve()
|
|
53
|
+
link_name = filename.removesuffix(".sh")
|
|
54
|
+
target_link = dest_dir / link_name
|
|
55
|
+
|
|
56
|
+
print(f"Examining: {filename}...")
|
|
57
|
+
|
|
58
|
+
if target_link.exists() or target_link.is_symlink():
|
|
59
|
+
if target_link.is_symlink():
|
|
60
|
+
current_target = target_link.resolve()
|
|
61
|
+
if current_target == abs_src:
|
|
62
|
+
print(f" [OK] Link already points correctly to {filename}.")
|
|
63
|
+
else:
|
|
64
|
+
print(" [UPDATE] Link points elsewhere. Updating...")
|
|
65
|
+
target_link.unlink()
|
|
66
|
+
target_link.symlink_to(abs_src)
|
|
67
|
+
else:
|
|
68
|
+
print(
|
|
69
|
+
f" [ERROR] '{target_link}' is a regular file. "
|
|
70
|
+
f"Skipping to avoid data loss.",
|
|
71
|
+
file=sys.stderr,
|
|
72
|
+
)
|
|
73
|
+
else:
|
|
74
|
+
print(" [NEW] Creating symlink...")
|
|
75
|
+
target_link.symlink_to(abs_src)
|
|
76
|
+
|
|
77
|
+
print("--- Operation completed ---")
|
|
78
|
+
return 0
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""@brief Claude CLI launcher in project context.
|
|
3
|
+
|
|
4
|
+
@details Ensures project-root prerequisite and executable availability before
|
|
5
|
+
blocking subprocess invocation of user-local Claude binary.
|
|
6
|
+
@satisfies REQ-017, REQ-055, REQ-056, REQ-064
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import subprocess
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from shell_scripts.utils import require_project_root, require_commands
|
|
13
|
+
|
|
14
|
+
## @var PROGRAM
|
|
15
|
+
# @brief Base CLI program name used in help output.
|
|
16
|
+
# @details Constant identifier for usage-line rendering in command help.
|
|
17
|
+
PROGRAM = "shellscripts"
|
|
18
|
+
## @var DESCRIPTION
|
|
19
|
+
# @brief One-line command description for dispatcher help surfaces.
|
|
20
|
+
# @details Exposed by command registry introspection (`get_all_commands`).
|
|
21
|
+
DESCRIPTION = "Launch Claude CLI with skip-permissions in the project context."
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def print_help(version):
|
|
25
|
+
"""@brief Print command-specific help for `claude`.
|
|
26
|
+
|
|
27
|
+
@details Emits usage and pass-through argument behavior for deterministic
|
|
28
|
+
terminal rendering; does not mutate process state.
|
|
29
|
+
@param version {str} CLI version string propagated by dispatcher.
|
|
30
|
+
@return {None} Writes help text to stdout.
|
|
31
|
+
@satisfies DES-008
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
print(f"Usage: {PROGRAM} claude [args...] ({version})")
|
|
35
|
+
print()
|
|
36
|
+
print("claude options:")
|
|
37
|
+
print(" All arguments are passed through to the Claude CLI.")
|
|
38
|
+
print(" --help - Show this help message.")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def run(args):
|
|
42
|
+
"""@brief Launch Claude CLI after external executable validation.
|
|
43
|
+
|
|
44
|
+
@details Resolves project root, resolves user-local Claude executable path,
|
|
45
|
+
validates executable availability, then executes command via subprocess.
|
|
46
|
+
@param args {list[str]} Additional CLI args forwarded to Claude.
|
|
47
|
+
@return {int} Child process return code.
|
|
48
|
+
@satisfies REQ-017, REQ-055, REQ-056, REQ-064
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
require_project_root()
|
|
52
|
+
claude_bin = Path.home() / ".claude" / "bin" / "claude"
|
|
53
|
+
cmd = [str(claude_bin), "--dangerously-skip-permissions"] + args
|
|
54
|
+
cmd[0] = require_commands(cmd[0])
|
|
55
|
+
result = subprocess.run(cmd)
|
|
56
|
+
return result.returncode
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from shell_scripts.utils import require_project_root, print_error
|
|
7
|
+
|
|
8
|
+
PROGRAM = "shellscripts"
|
|
9
|
+
DESCRIPTION = "Find and delete cache directories under the project root."
|
|
10
|
+
|
|
11
|
+
CACHE_DIRS = [
|
|
12
|
+
"__pycache__",
|
|
13
|
+
".pytest_cache",
|
|
14
|
+
".mypy_cache",
|
|
15
|
+
".cache",
|
|
16
|
+
".npm",
|
|
17
|
+
".parcel-cache",
|
|
18
|
+
".eslintcache",
|
|
19
|
+
".sass-cache",
|
|
20
|
+
".terragrunt-cache",
|
|
21
|
+
"htmlcov",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def print_help(version):
|
|
26
|
+
print(f"Usage: {PROGRAM} clean [options] ({version})")
|
|
27
|
+
print()
|
|
28
|
+
print("clean options:")
|
|
29
|
+
print(" <directory> - Target directory to search (default: git project root).")
|
|
30
|
+
print(" --yes - Skip confirmation prompt and delete immediately.")
|
|
31
|
+
print(" --help - Show this help message.")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def run(args):
|
|
35
|
+
project_root = require_project_root()
|
|
36
|
+
target_dir = project_root
|
|
37
|
+
auto_confirm = False
|
|
38
|
+
|
|
39
|
+
for arg in args:
|
|
40
|
+
if arg == "--yes":
|
|
41
|
+
auto_confirm = True
|
|
42
|
+
elif not arg.startswith("-"):
|
|
43
|
+
target_dir = Path(arg)
|
|
44
|
+
else:
|
|
45
|
+
print_error(f"Unknown option: {arg}")
|
|
46
|
+
return 1
|
|
47
|
+
|
|
48
|
+
os.chdir(project_root)
|
|
49
|
+
|
|
50
|
+
print(f"--- Searching for cache directories in: {target_dir} ---")
|
|
51
|
+
print("Please wait, scanning...")
|
|
52
|
+
|
|
53
|
+
found = []
|
|
54
|
+
for dir_name in CACHE_DIRS:
|
|
55
|
+
for root, dirs, _ in os.walk(target_dir):
|
|
56
|
+
if dir_name in dirs:
|
|
57
|
+
found.append(os.path.join(root, dir_name))
|
|
58
|
+
|
|
59
|
+
if not found:
|
|
60
|
+
print("No cache directories found.")
|
|
61
|
+
return 0
|
|
62
|
+
|
|
63
|
+
print()
|
|
64
|
+
print("Found the following directories:")
|
|
65
|
+
print("---------------------------------")
|
|
66
|
+
for path in found:
|
|
67
|
+
print(f" -> {path}")
|
|
68
|
+
print("---------------------------------")
|
|
69
|
+
print(f"Total directories found: {len(found)}")
|
|
70
|
+
print()
|
|
71
|
+
|
|
72
|
+
if auto_confirm:
|
|
73
|
+
confirm = "y"
|
|
74
|
+
else:
|
|
75
|
+
try:
|
|
76
|
+
confirm = input(
|
|
77
|
+
"WARNING: Are you sure you want to permanently DELETE "
|
|
78
|
+
"these directories? (y/N): "
|
|
79
|
+
)
|
|
80
|
+
except (EOFError, KeyboardInterrupt):
|
|
81
|
+
print()
|
|
82
|
+
confirm = "n"
|
|
83
|
+
|
|
84
|
+
if confirm.lower() == "y":
|
|
85
|
+
print()
|
|
86
|
+
print("Deleting...")
|
|
87
|
+
count = 0
|
|
88
|
+
for path in found:
|
|
89
|
+
shutil.rmtree(path, ignore_errors=True)
|
|
90
|
+
count += 1
|
|
91
|
+
print(f"Deleted: {path}")
|
|
92
|
+
print()
|
|
93
|
+
print(f"Cleanup complete! {count} directories removed.")
|
|
94
|
+
else:
|
|
95
|
+
print()
|
|
96
|
+
print("Operation aborted. No files were touched.")
|
|
97
|
+
|
|
98
|
+
return 0
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""@brief Codex CLI launcher with project-context auth file synchronization.
|
|
3
|
+
|
|
4
|
+
@details Ensures project-local Codex context is prepared before command
|
|
5
|
+
execution. The launcher copies auth state from `~/.codex/auth.json` into
|
|
6
|
+
`<project>/.codex/auth.json` before CLI launch, sets
|
|
7
|
+
`CODEX_HOME=<project>/.codex`, executes `codex --yolo`, then copies auth state
|
|
8
|
+
back from project path to home path before returning, emitting one
|
|
9
|
+
informational output line for each copy direction.
|
|
10
|
+
@satisfies REQ-014, REQ-043, REQ-044, REQ-064
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from shell_scripts.utils import print_info, require_project_root, require_commands
|
|
19
|
+
|
|
20
|
+
## @var PROGRAM
|
|
21
|
+
# @brief Base CLI program name used in help output.
|
|
22
|
+
# @details Constant identifier for usage-line rendering in command help.
|
|
23
|
+
PROGRAM = "shellscripts"
|
|
24
|
+
## @var DESCRIPTION
|
|
25
|
+
# @brief One-line command description for dispatcher help surfaces.
|
|
26
|
+
# @details Exposed by command registry introspection (`get_all_commands`).
|
|
27
|
+
DESCRIPTION = "Launch OpenAI Codex CLI in the project context."
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def print_help(version: str) -> None:
|
|
31
|
+
"""@brief Print command-specific help for `codex`.
|
|
32
|
+
|
|
33
|
+
@details Emits usage and pass-through argument behavior for deterministic
|
|
34
|
+
terminal rendering; does not mutate process state.
|
|
35
|
+
@param version {str} CLI version string propagated by dispatcher.
|
|
36
|
+
@return {None} Writes help text to stdout.
|
|
37
|
+
@satisfies DES-008
|
|
38
|
+
"""
|
|
39
|
+
print(f"Usage: {PROGRAM} codex [args...] ({version})")
|
|
40
|
+
print()
|
|
41
|
+
print("codex options:")
|
|
42
|
+
print(" All arguments are passed through to the Codex CLI.")
|
|
43
|
+
print(" --help - Show this help message.")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _copy_auth_file(
|
|
47
|
+
source_path: Path,
|
|
48
|
+
destination_path: Path,
|
|
49
|
+
direction_label: str,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""@brief Copy Codex auth file while replacing destination file or symlink.
|
|
52
|
+
|
|
53
|
+
@details Ensures destination parent directory exists, removes an existing
|
|
54
|
+
destination entry when it is a file or symbolic link, then copies source
|
|
55
|
+
bytes to destination preserving metadata with `shutil.copy2`, then emits
|
|
56
|
+
one informational output line for copy evidence. Time complexity O(n) where
|
|
57
|
+
n is auth file size.
|
|
58
|
+
@param source_path {Path} Existing auth file source path.
|
|
59
|
+
@param destination_path {Path} Auth file destination path to overwrite.
|
|
60
|
+
@param direction_label {str} Copy direction descriptor shown in info output.
|
|
61
|
+
@return {None} Applies destination filesystem mutation.
|
|
62
|
+
@throws {OSError} If source read, destination unlink, or copy operation fails.
|
|
63
|
+
@satisfies REQ-043, REQ-044
|
|
64
|
+
"""
|
|
65
|
+
destination_path.parent.mkdir(parents=True, exist_ok=True)
|
|
66
|
+
if destination_path.exists() or destination_path.is_symlink():
|
|
67
|
+
destination_path.unlink()
|
|
68
|
+
shutil.copy2(source_path, destination_path)
|
|
69
|
+
print_info(
|
|
70
|
+
f"Copied auth.json ({direction_label}): {source_path} -> {destination_path}"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def run(args: list[str]) -> int:
|
|
75
|
+
"""@brief Launch Codex CLI with project-scoped environment preparation.
|
|
76
|
+
|
|
77
|
+
@details Resolves project root, copies auth from home into project auth
|
|
78
|
+
file, emits copy evidence output, sets `CODEX_HOME=<project-root>/.codex`,
|
|
79
|
+
executes `codex --yolo` plus pass-through args through blocking subprocess
|
|
80
|
+
run, then copies project auth back to home path in a `finally` block with
|
|
81
|
+
reverse-direction copy evidence output.
|
|
82
|
+
@param args {list[str]} Additional CLI args forwarded to Codex.
|
|
83
|
+
@return {int} Child process return code.
|
|
84
|
+
@throws {OSError} Propagated for auth-file copy or process-launch failures.
|
|
85
|
+
@satisfies REQ-014, REQ-043, REQ-044, REQ-064
|
|
86
|
+
"""
|
|
87
|
+
project_root = require_project_root()
|
|
88
|
+
project_auth_path = project_root / ".codex" / "auth.json"
|
|
89
|
+
home_auth_path = Path.home() / ".codex" / "auth.json"
|
|
90
|
+
_copy_auth_file(
|
|
91
|
+
home_auth_path,
|
|
92
|
+
project_auth_path,
|
|
93
|
+
"global config -> project config",
|
|
94
|
+
)
|
|
95
|
+
codex_home = str(project_root / ".codex")
|
|
96
|
+
os.environ["CODEX_HOME"] = codex_home
|
|
97
|
+
cmd = ["codex", "--yolo"] + args
|
|
98
|
+
cmd[0] = require_commands(cmd[0])
|
|
99
|
+
try:
|
|
100
|
+
result = subprocess.run(cmd)
|
|
101
|
+
finally:
|
|
102
|
+
_copy_auth_file(
|
|
103
|
+
project_auth_path,
|
|
104
|
+
home_auth_path,
|
|
105
|
+
"project config -> global config",
|
|
106
|
+
)
|
|
107
|
+
return result.returncode
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""@brief GitHub Copilot CLI launcher in project context.
|
|
3
|
+
|
|
4
|
+
@details Ensures project-root prerequisite and executable availability before
|
|
5
|
+
blocking subprocess invocation of `copilot`.
|
|
6
|
+
@satisfies REQ-015, REQ-055, REQ-056, REQ-064
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import subprocess
|
|
10
|
+
|
|
11
|
+
from shell_scripts.utils import require_project_root, require_commands
|
|
12
|
+
|
|
13
|
+
## @var PROGRAM
|
|
14
|
+
# @brief Base CLI program name used in help output.
|
|
15
|
+
# @details Constant identifier for usage-line rendering in command help.
|
|
16
|
+
PROGRAM = "shellscripts"
|
|
17
|
+
## @var DESCRIPTION
|
|
18
|
+
# @brief One-line command description for dispatcher help surfaces.
|
|
19
|
+
# @details Exposed by command registry introspection (`get_all_commands`).
|
|
20
|
+
DESCRIPTION = "Launch GitHub Copilot CLI in the project context."
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def print_help(version):
|
|
24
|
+
"""@brief Print command-specific help for `copilot`.
|
|
25
|
+
|
|
26
|
+
@details Emits usage and pass-through argument behavior for deterministic
|
|
27
|
+
terminal rendering; does not mutate process state.
|
|
28
|
+
@param version {str} CLI version string propagated by dispatcher.
|
|
29
|
+
@return {None} Writes help text to stdout.
|
|
30
|
+
@satisfies DES-008
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
print(f"Usage: {PROGRAM} copilot [args...] ({version})")
|
|
34
|
+
print()
|
|
35
|
+
print("copilot options:")
|
|
36
|
+
print(" All arguments are passed through to the Copilot CLI.")
|
|
37
|
+
print(" --help - Show this help message.")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def run(args):
|
|
41
|
+
"""@brief Launch Copilot CLI after external executable validation.
|
|
42
|
+
|
|
43
|
+
@details Resolves project root, checks executable availability for
|
|
44
|
+
`copilot`, then executes pass-through args through blocking subprocess run.
|
|
45
|
+
@param args {list[str]} Additional CLI args forwarded to Copilot.
|
|
46
|
+
@return {int} Child process return code.
|
|
47
|
+
@satisfies REQ-015, REQ-055, REQ-056, REQ-064
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
require_project_root()
|
|
51
|
+
cmd = ["copilot", "--yolo", "--allow-all-tools", "--no-auto-update"] + args
|
|
52
|
+
cmd[0] = require_commands(cmd[0])
|
|
53
|
+
result = subprocess.run(cmd)
|
|
54
|
+
return result.returncode
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""@brief PixelMed DICOM-to-JPEG converter launcher.
|
|
3
|
+
|
|
4
|
+
@details Resolves Java runtime and classpath prerequisites and validates Java
|
|
5
|
+
executable availability before subprocess invocation.
|
|
6
|
+
@satisfies REQ-026, REQ-055, REQ-056
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
import shutil
|
|
12
|
+
|
|
13
|
+
from shell_scripts.utils import print_error, require_commands
|
|
14
|
+
|
|
15
|
+
PROGRAM = "shellscripts"
|
|
16
|
+
DESCRIPTION = "Convert DICOM images to JPEG using PixelMed."
|
|
17
|
+
|
|
18
|
+
JAVA_WRAPPERS = "/usr/lib/java-wrappers/java-wrappers.sh"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def print_help(version):
|
|
22
|
+
print(f"Usage: {PROGRAM} dicom2jpg <input.dcm> <output.jpg> ({version})")
|
|
23
|
+
print()
|
|
24
|
+
print("dicom2jpg options:")
|
|
25
|
+
print(" <input.dcm> - Input DICOM file (required).")
|
|
26
|
+
print(" <output.jpg> - Output JPEG file (required).")
|
|
27
|
+
print(" --help - Show this help message.")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _find_java():
|
|
31
|
+
for java in ("java", "/usr/bin/java"):
|
|
32
|
+
if shutil.which(java):
|
|
33
|
+
return java
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _find_jars(*jar_names):
|
|
38
|
+
jar_dirs = ["/usr/share/java", "/usr/local/share/java"]
|
|
39
|
+
found = []
|
|
40
|
+
for name in jar_names:
|
|
41
|
+
for d in jar_dirs:
|
|
42
|
+
jar_path = os.path.join(d, f"{name}.jar")
|
|
43
|
+
if os.path.exists(jar_path):
|
|
44
|
+
found.append(jar_path)
|
|
45
|
+
break
|
|
46
|
+
return found
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def run(args):
|
|
50
|
+
"""@brief Run PixelMed converter after executable validation.
|
|
51
|
+
|
|
52
|
+
@details Validates Java runtime availability and command executability for
|
|
53
|
+
the selected Java binary before invoking conversion class.
|
|
54
|
+
@param args {list[str]} Expected `[input_dicom, output_jpeg]` arguments.
|
|
55
|
+
@return {int} Subprocess return code.
|
|
56
|
+
@satisfies REQ-026, REQ-055, REQ-056
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
if len(args) < 2:
|
|
60
|
+
print_error("Usage: dicom2jpg <input.dcm> <output.jpg>")
|
|
61
|
+
return 1
|
|
62
|
+
|
|
63
|
+
input_file = args[0]
|
|
64
|
+
output_file = args[1]
|
|
65
|
+
|
|
66
|
+
if not os.path.exists(JAVA_WRAPPERS):
|
|
67
|
+
print_error("java-wrappers not found. Install the 'java-wrappers' package.")
|
|
68
|
+
return 1
|
|
69
|
+
|
|
70
|
+
java = _find_java()
|
|
71
|
+
if not java:
|
|
72
|
+
print_error("Java runtime not found.")
|
|
73
|
+
return 1
|
|
74
|
+
|
|
75
|
+
jars = _find_jars(
|
|
76
|
+
"pixelmed",
|
|
77
|
+
"hsqldb",
|
|
78
|
+
"vecmath",
|
|
79
|
+
"jmdns",
|
|
80
|
+
"commons-codec",
|
|
81
|
+
"jai_imageio",
|
|
82
|
+
"clibwrapper_jiio",
|
|
83
|
+
)
|
|
84
|
+
classpath = ":".join(jars) if jars else ""
|
|
85
|
+
|
|
86
|
+
cmd = [
|
|
87
|
+
java,
|
|
88
|
+
"-Djava.awt.headless=true",
|
|
89
|
+
"-cp",
|
|
90
|
+
classpath,
|
|
91
|
+
"com.pixelmed.display.ConsumerFormatImageMaker",
|
|
92
|
+
input_file,
|
|
93
|
+
output_file,
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
require_commands(cmd[0])
|
|
97
|
+
result = subprocess.run(cmd)
|
|
98
|
+
return result.returncode
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""@brief PixelMed DICOM viewer launcher.
|
|
3
|
+
|
|
4
|
+
@details Resolves Java runtime and classpath prerequisites and validates Java
|
|
5
|
+
executable availability before subprocess invocation.
|
|
6
|
+
@satisfies REQ-025, REQ-055, REQ-056
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
import shutil
|
|
12
|
+
|
|
13
|
+
from shell_scripts.utils import print_error, require_commands
|
|
14
|
+
|
|
15
|
+
PROGRAM = "shellscripts"
|
|
16
|
+
DESCRIPTION = "Launch PixelMed DICOM image viewer."
|
|
17
|
+
|
|
18
|
+
JAVA_WRAPPERS = "/usr/lib/java-wrappers/java-wrappers.sh"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def print_help(version):
|
|
22
|
+
print(f"Usage: {PROGRAM} dicomviewer [files...] ({version})")
|
|
23
|
+
print()
|
|
24
|
+
print("dicomviewer options:")
|
|
25
|
+
print(" [files...] - DICOM files to view.")
|
|
26
|
+
print(" --help - Show this help message.")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _find_java():
|
|
30
|
+
for java in ("java", "/usr/bin/java"):
|
|
31
|
+
if shutil.which(java):
|
|
32
|
+
return java
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _find_jars(*jar_names):
|
|
37
|
+
jar_dirs = ["/usr/share/java", "/usr/local/share/java"]
|
|
38
|
+
found = []
|
|
39
|
+
for name in jar_names:
|
|
40
|
+
for d in jar_dirs:
|
|
41
|
+
jar_path = os.path.join(d, f"{name}.jar")
|
|
42
|
+
if os.path.exists(jar_path):
|
|
43
|
+
found.append(jar_path)
|
|
44
|
+
break
|
|
45
|
+
return found
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def run(args):
|
|
49
|
+
"""@brief Run PixelMed DICOM viewer after executable validation.
|
|
50
|
+
|
|
51
|
+
@details Validates Java runtime availability and command executability for
|
|
52
|
+
the selected Java binary before invoking the viewer class.
|
|
53
|
+
@param args {list[str]} DICOM file arguments forwarded to PixelMed viewer.
|
|
54
|
+
@return {int} Subprocess return code.
|
|
55
|
+
@satisfies REQ-025, REQ-055, REQ-056
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
if not os.path.exists(JAVA_WRAPPERS):
|
|
59
|
+
print_error(
|
|
60
|
+
"java-wrappers not found. Install the 'java-wrappers' package."
|
|
61
|
+
)
|
|
62
|
+
return 1
|
|
63
|
+
|
|
64
|
+
java = _find_java()
|
|
65
|
+
if not java:
|
|
66
|
+
print_error("Java runtime not found.")
|
|
67
|
+
return 1
|
|
68
|
+
|
|
69
|
+
jars = _find_jars(
|
|
70
|
+
"pixelmed", "hsqldb", "vecmath", "jmdns",
|
|
71
|
+
"commons-codec", "jai_imageio", "clibwrapper_jiio"
|
|
72
|
+
)
|
|
73
|
+
classpath = ":".join(jars) if jars else ""
|
|
74
|
+
|
|
75
|
+
cmd = [
|
|
76
|
+
java, "-Djava.awt.headless=true",
|
|
77
|
+
"-cp", classpath,
|
|
78
|
+
"com.pixelmed.display.DicomImageViewer",
|
|
79
|
+
] + args
|
|
80
|
+
|
|
81
|
+
require_commands(cmd[0])
|
|
82
|
+
result = subprocess.run(cmd)
|
|
83
|
+
return result.returncode
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""@brief MIME-based diff command wrapper.
|
|
3
|
+
|
|
4
|
+
@details Exposes the `diff` CLI command and delegates category-based executable
|
|
5
|
+
selection to shared `_dc_common.dispatch` logic.
|
|
6
|
+
@satisfies PRJ-003, DES-007, REQ-023, REQ-024
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from shell_scripts.config import get_dispatch_profile
|
|
12
|
+
from shell_scripts.commands._dc_common import dispatch
|
|
13
|
+
|
|
14
|
+
PROGRAM = "shellscripts"
|
|
15
|
+
DESCRIPTION = "File differ dispatcher by MIME type."
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def print_help(version):
|
|
19
|
+
"""@brief Render command help for `diff`.
|
|
20
|
+
|
|
21
|
+
@details Prints usage, required file argument semantics, and argument
|
|
22
|
+
forwarding contract for the selected external diff executable.
|
|
23
|
+
@param version {str} Version string appended in usage output.
|
|
24
|
+
@return {None} Writes help text to stdout.
|
|
25
|
+
@satisfies DES-008
|
|
26
|
+
"""
|
|
27
|
+
print(f"Usage: {PROGRAM} diff <file> [args...] ({version})")
|
|
28
|
+
print()
|
|
29
|
+
print("diff options:")
|
|
30
|
+
print(" <file> - File to diff (required).")
|
|
31
|
+
print(" Additional arguments are passed to the selected diff tool.")
|
|
32
|
+
print(" --help - Show this help message.")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def run(args):
|
|
36
|
+
"""@brief Execute MIME-routed diff dispatch.
|
|
37
|
+
|
|
38
|
+
@details Validates that a file argument exists; on missing argument prints
|
|
39
|
+
error plus help and returns status code `2`; otherwise resolves runtime
|
|
40
|
+
dispatch profile and delegates by file category through shared `_dc_common`.
|
|
41
|
+
@param args {list[str]} CLI args where `args[0]` is file path.
|
|
42
|
+
@return {int} Return code `2` on missing file; otherwise delegated dispatch result.
|
|
43
|
+
@satisfies REQ-023, REQ-024
|
|
44
|
+
"""
|
|
45
|
+
if not args:
|
|
46
|
+
print("Error: file argument required.", file=sys.stderr)
|
|
47
|
+
print_help("")
|
|
48
|
+
return 2
|
|
49
|
+
category_cmds, fallback = get_dispatch_profile("diff")
|
|
50
|
+
return dispatch(category_cmds, fallback, args[0], args[1:])
|