Skip to content

Commit ebee915

Browse files
committed
urly work
1 parent 3a5eb3d commit ebee915

4 files changed

Lines changed: 154 additions & 20 deletions

File tree

bin/gen_manpages.sh

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,20 @@ else
2121
echo "INFO: using existing manpage directory ${MANPAGE_DIR}"
2222
fi
2323

24-
FILES=$(ls "${REPO_DIR}/cmd")
24+
PROGRAMS=$(ls "${REPO_DIR}/cmd")
2525

26-
for f in $FILES; do
27-
if [ -f "${MANPAGE_DIR}/${f}" ]; then
28-
echo "WARNING: file ${MANPAGE_DIR}/${f} already exists"
26+
for PROGRAM in $PROGRAMS; do
27+
if [ -f "${MANPAGE_DIR}/${PROGRAM}.1" ]; then
28+
echo "WARNING: file ${MANPAGE_DIR}/${PROGRAM}.1 already exists"
2929
continue
3030
fi
31-
echo "INFO: compiling ${f}"
32-
#LATER: go build -o "${MANPAGE_DIR}/${f}" "${REPO_DIR}/cmd/${f}"
31+
echo "INFO: compiling ${PROGRAM} manpage to ${MANPAGE_DIR}/${PROGRAM}.1"
32+
if [ ! -f "${REPO_DIR}/cmd/${PROGRAM}/README.md" ]; then
33+
echo "WARNING: missing README.md for ${PROGRAM}, skipping"
34+
continue
35+
fi
36+
# generate manpage from README.md
37+
pandoc --standalone --to man "${REPO_DIR}/cmd/${PROGRAM}/README.md" -o "${MANPAGE_DIR}/${PROGRAM}.1"
3338
done
3439

3540
echo "INFO: complete at $(date -u +%Y-%m-%dT%H:%M:%SZ)"

cmd/uniwhat/README.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,33 @@
1-
# Uniwhat
1+
% UNIWHAT(1)
2+
% Andrew Marcuse
3+
% 2025-12-17
4+
5+
# NAME
6+
7+
uniwhat - figures out what Unicode characters are in a file
8+
9+
# SYNOPSIS
10+
11+
# DESCRIPTION
12+
13+
# ENVIRONMENT
14+
15+
# EXIT STATUS
16+
17+
# EXAMPLES
18+
19+
# SEE ALSO
20+
21+
# STANDARDS
22+
23+
# HISTORY
24+
25+
# AUTHORS
26+
27+
# HISTORY
28+
29+
# SECURITY CONSIDERATIONS
30+
...
31+
232

333

cmd/urly/urly.go

Lines changed: 110 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func setPassword(userInfo *url.Userinfo, password string) *url.Userinfo {
3434

3535
type UrlJson struct {
3636
Scheme string `json:"scheme"`
37+
Hostname string `json:"hostname"`
3738
Host string `json:"host"`
3839
Port string `json:"port"`
3940
Path string `json:"path"`
@@ -45,10 +46,17 @@ type UrlJson struct {
4546
Params map[string][]string `json:"params,omitempty"`
4647
}
4748

48-
func toJson(theUrl *url.URL) string {
49+
func toJson(theUrl *url.URL, pretty bool) string {
50+
var theHost string
51+
if theUrl.Port() != "" {
52+
theHost = fmt.Sprintf("%s:%s", theUrl.Hostname(), theUrl.Port())
53+
} else {
54+
theHost = theUrl.Hostname()
55+
}
4956
urlJson := UrlJson{
5057
Scheme: theUrl.Scheme,
51-
Host: theUrl.Hostname(),
58+
Hostname: theUrl.Hostname(),
59+
Host: theHost,
5260
Port: theUrl.Port(),
5361
Path: theUrl.Path,
5462
Query: theUrl.RawQuery,
@@ -63,7 +71,13 @@ func toJson(theUrl *url.URL) string {
6371
urlJson.Password = password
6472
}
6573
}
66-
jsonStr, err := json.Marshal(urlJson)
74+
var jsonStr []byte
75+
var err error
76+
if pretty {
77+
jsonStr, err = json.MarshalIndent(urlJson, "", " ")
78+
} else {
79+
jsonStr, err = json.Marshal(urlJson)
80+
}
6781
if err != nil {
6882
fmt.Fprintf(os.Stderr, "ERROR: unable to marshal URL to JSON: %v\n", err)
6983
os.Exit(1)
@@ -77,14 +91,32 @@ var helpText = `urly: A URL parsing and processing tool.`
7791
func main() {
7892

7993
var scheme = pflag.String("scheme", "", "Set the URL scheme")
94+
var noScheme = pflag.Bool("no-scheme", false, "Remove the URL scheme")
95+
96+
var envUsername = pflag.String("username-env", "", "Environment variable containing the username for URL processing")
97+
var textUsername = pflag.String("username", "", "Username for URL processing")
98+
var noUsername = pflag.Bool("no-username", false, "Remove the username from the URL")
99+
80100
var envPassword = pflag.String("password-env", "", "Environment variable containing the password for URL processing")
81101
var stdinPassword = pflag.Bool("password-stdin", false, "Read password from standard input")
102+
var noPassword = pflag.Bool("no-password", false, "Remove the password from the URL")
103+
104+
var hostname = pflag.String("hostname", "", "Set the URL hostname")
105+
var noHostname = pflag.Bool("no-hostname", false, "Remove the URL hostname")
106+
var port = pflag.String("port", "", "Set the URL port")
107+
var noPort = pflag.Bool("no-port", false, "Remove the URL port")
108+
var path = pflag.String("path", "", "Set the URL path")
109+
var noPath = pflag.Bool("no-path", false, "Remove the URL path")
110+
var query = pflag.String("query", "", "Set the URL query")
111+
var noQuery = pflag.Bool("no-query", false, "Remove the URL query")
112+
var fragment = pflag.String("fragment", "", "Set the URL fragment")
113+
var noFragment = pflag.Bool("no-fragment", false, "Remove the URL fragment")
114+
82115
var envUrl = pflag.String("url-env", "", "Environment variable containing the URL to process")
83-
var envUsername = pflag.String("username-env", "", "Environment variable containing the username for URL processing")
84-
var textUsername = pflag.String("username", "", "Username for URL processing")
85-
//LATER: var format = pflag.String("format", "text", "Output format: text or json")
116+
86117
var output = pflag.String("output", "url", "Output type: url, scheme, host, port, path, query, fragment, userinfo, username, password")
87118
var newline = pflag.Bool("newline", false, "Append newline to output")
119+
88120
var help = pflag.Bool("help", false, "Detailed help")
89121
var version = pflag.Bool("version", false, "Version info")
90122

@@ -96,7 +128,10 @@ func main() {
96128
}
97129

98130
if *help {
99-
fmt.Printf("%s\n", helpText)
131+
fmt.Println("urly - manipulate URLs")
132+
pflag.PrintDefaults()
133+
fmt.Println()
134+
fmt.Println("Use `man urly` for detailed help.")
100135
return
101136
}
102137

@@ -128,13 +163,35 @@ func main() {
128163
theUrl = &url.URL{}
129164
}
130165

131-
if *envUsername != "" {
166+
if *noScheme {
167+
theUrl.Scheme = ""
168+
} else if *scheme != "" {
169+
theUrl.Scheme = *scheme
170+
}
171+
172+
if *noUsername {
173+
if theUrl.User != nil {
174+
thePassword, hasPassword := theUrl.User.Password()
175+
if hasPassword {
176+
theUrl.User = url.UserPassword("", thePassword)
177+
} else {
178+
theUrl.User = nil
179+
}
180+
}
181+
} else if *envUsername != "" {
132182
theUrl.User = setUserName(theUrl.User, os.Getenv(*envUsername))
133183
} else if *textUsername != "" {
134184
theUrl.User = setUserName(theUrl.User, *textUsername)
135185
}
136186

137-
if *envPassword != "" {
187+
if *noPassword {
188+
if theUrl.User != nil {
189+
theUsername := theUrl.User.Username()
190+
theUrl.User = url.User(theUsername)
191+
} else {
192+
theUrl.User = nil
193+
}
194+
} else if *envPassword != "" {
138195
theUrl.User = setPassword(theUrl.User, os.Getenv(*envPassword))
139196
} else if *stdinPassword {
140197
var password string
@@ -146,10 +203,50 @@ func main() {
146203
theUrl.User = setPassword(theUrl.User, password)
147204
}
148205

149-
if *scheme != "" {
206+
if *noScheme {
207+
theUrl.Scheme = ""
208+
} else if *scheme != "" {
150209
theUrl.Scheme = *scheme
151210
}
152211

212+
if *noHostname {
213+
if theUrl.Port() != "" {
214+
theUrl.Host = ":" + theUrl.Port()
215+
} else {
216+
theUrl.Host = ""
217+
}
218+
} else if *hostname != "" {
219+
if theUrl.Port() != "" {
220+
theUrl.Host = *hostname + ":" + theUrl.Port()
221+
} else {
222+
theUrl.Host = *hostname
223+
}
224+
}
225+
226+
if *noPort {
227+
theUrl.Host = theUrl.Hostname()
228+
} else if *port != "" {
229+
theUrl.Host = fmt.Sprintf("%s:%s", theUrl.Hostname(), *port)
230+
}
231+
232+
if *noPath {
233+
theUrl.Path = ""
234+
} else if *path != "" {
235+
theUrl.Path = *path
236+
}
237+
238+
if *noQuery {
239+
theUrl.RawQuery = ""
240+
} else if *query != "" {
241+
theUrl.RawQuery = *query
242+
}
243+
244+
if *noFragment {
245+
theUrl.Fragment = ""
246+
} else if *fragment != "" {
247+
theUrl.Fragment = *fragment
248+
}
249+
153250
switch *output {
154251
case "url":
155252
fmt.Println(theUrl.String())
@@ -186,7 +283,9 @@ func main() {
186283
}
187284
}
188285
case "json":
189-
fmt.Print(toJson(theUrl))
286+
fmt.Print(toJson(theUrl, true))
287+
case "jsonl":
288+
fmt.Print(toJson(theUrl, false))
190289
default:
191290
fmt.Fprintf(os.Stderr, "ERROR: Unknown output type: %s\n", *output)
192291
os.Exit(1)

testdata/urly.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ exec urly --scheme=postgresql postgres://user@server.example.com/db
1919
stdout 'postgresql://user@server\.example\.com/db'
2020

2121
# json output
22-
exec urly --output=json https://u:p@example.com/path/to/file.ext?param=value
23-
stdout '{"scheme":"https","host":"example\.com","port":"","path":"/path/to/file\.ext","query":"param=value","fragment":"","username":"u","password":"p","url":"https://u:p@example\.com/path/to/file\.ext\?param=value","params":{"param":\["value"\]}}'
22+
exec urly --output=jsonl https://u:p@example.com/path/to/file.ext?param=value
23+
stdout '{"scheme":"https","hostname":"example\.com","host":"example\.com","port":"","path":"/path/to/file\.ext","query":"param=value","fragment":"","username":"u","password":"p","url":"https://u:p@example\.com/path/to/file\.ext\?param=value","params":{"param":\["value"\]}}'
2424

2525
# add a parameter
2626
# remove a parameter

0 commit comments

Comments
 (0)