Skip to content

Commit 0c98839

Browse files
committed
readlink: Add readlink.py
1 parent eed796f commit 0c98839

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

src/readlink.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/python3
2+
3+
import sys
4+
from optparse import OptionParser
5+
from pathlib import Path
6+
from typing import Callable
7+
8+
9+
def readlink_function(can_mode: str | None) -> Callable[[Path], str]:
10+
match can_mode:
11+
case None:
12+
return lambda path: path.readlink()
13+
case "f":
14+
return (
15+
lambda path: path.parent.resolve(strict=True)
16+
.joinpath(path.name)
17+
.resolve(strict=False)
18+
)
19+
case "e" | "m":
20+
return lambda path: path.resolve(strict=can_mode == "e")
21+
22+
23+
def readlink(opts, paths: list[Path]):
24+
# This is the precise behavior of GNU readlink regardless
25+
# of in what order the -n and -z flags are specified.
26+
endchar = "" if opts.no_newline else "\0" if opts.zero else "\n"
27+
28+
func = readlink_function(opts.can_mode)
29+
failed = False
30+
31+
for path in paths:
32+
try:
33+
print(func(path), end=endchar)
34+
except OSError as e:
35+
failed = True
36+
37+
if opts.verbose:
38+
print(e, file=sys.stderr)
39+
40+
if failed:
41+
sys.exit(1)
42+
43+
44+
if __name__ == "__main__":
45+
parser = OptionParser(
46+
usage="Usage: %prog [OPTION]... FILE...",
47+
description="Print the target of each symbolic link FILE.",
48+
add_help_option=False,
49+
)
50+
parser.add_option("--help", action="help", help="show usage information and exit")
51+
52+
parser.add_option(
53+
"-f",
54+
"--canonicalize",
55+
dest="can_mode",
56+
action="store_const",
57+
const="f",
58+
help="all but the last path component must exist",
59+
)
60+
parser.add_option(
61+
"-e",
62+
"--canonicalize-existing",
63+
dest="can_mode",
64+
action="store_const",
65+
const="e",
66+
help="all path components must exist",
67+
)
68+
parser.add_option(
69+
"-m",
70+
"--canonicalize-missing",
71+
dest="can_mode",
72+
action="store_const",
73+
const="m",
74+
help="no path components need exist or be a directory",
75+
)
76+
77+
parser.add_option(
78+
"-n",
79+
"--no-newline",
80+
action="store_true",
81+
help="do not delimit outputs (overrides -z)",
82+
)
83+
parser.add_option(
84+
"-z",
85+
"--zero",
86+
action="store_true",
87+
help="terminate outputs with NUL instead of newline",
88+
)
89+
90+
parser.add_option(
91+
"-q",
92+
"--quiet",
93+
"-s",
94+
"--silent",
95+
dest="verbose",
96+
action="store_false",
97+
default=False,
98+
help="suppress error messages (default)",
99+
)
100+
parser.add_option(
101+
"-v",
102+
"--verbose",
103+
dest="verbose",
104+
action="store_true",
105+
help="report errors",
106+
)
107+
108+
opts, args = parser.parse_args()
109+
110+
if opts.no_newline and len(args) > 1:
111+
print("ignoring --no-newline with multiple arguments", file=sys.stderr)
112+
opts.no_newline = False
113+
114+
readlink(opts, map(Path, args))

0 commit comments

Comments
 (0)