Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
ccan
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
mirror
ccan
Commits
3d45cf27
Commit
3d45cf27
authored
Dec 19, 2011
by
Rusty Russell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
opt: much prettier usage (using terminal size)
parent
fba46ae0
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
381 additions
and
70 deletions
+381
-70
ccan/opt/test/run-add_desc.c
ccan/opt/test/run-add_desc.c
+164
-0
ccan/opt/test/run-consume_words.c
ccan/opt/test/run-consume_words.c
+37
-0
ccan/opt/test/run-helpers.c
ccan/opt/test/run-helpers.c
+1
-1
ccan/opt/test/run-usage.c
ccan/opt/test/run-usage.c
+9
-0
ccan/opt/usage.c
ccan/opt/usage.c
+170
-69
No files found.
ccan/opt/test/run-add_desc.c
0 → 100644
View file @
3d45cf27
#include <ccan/tap/tap.h>
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
#include <ccan/opt/parse.c>
static
void
show_10
(
char
buf
[
OPT_SHOW_LEN
],
const
void
*
arg
)
{
memset
(
buf
,
'X'
,
10
);
buf
[
10
]
=
'\0'
;
}
static
void
show_max
(
char
buf
[
OPT_SHOW_LEN
],
const
void
*
arg
)
{
memset
(
buf
,
'X'
,
OPT_SHOW_LEN
);
}
/* Test add_desc helper. */
int
main
(
int
argc
,
char
*
argv
[])
{
struct
opt_table
opt
;
char
*
ret
;
size_t
len
,
max
;
plan_tests
(
30
);
opt
.
show
=
NULL
;
opt
.
names
=
"01234"
;
opt
.
desc
=
"0123456789 0"
;
opt
.
type
=
OPT_NOARG
;
len
=
max
=
0
;
/* Fits easily. */
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
10
,
30
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 0123456789 0
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* Name just fits. */
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
7
,
30
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 0123456789 0
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* Name doesn't fit. */
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
6
,
30
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234
\n
"
" 0123456789 0
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* Description just fits. */
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
7
,
19
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 0123456789 0
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* Description doesn't quite fit. */
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
7
,
18
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 0123456789
\n
"
" 0
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* Neither quite fits. */
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
6
,
17
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234
\n
"
" 0123456789
\n
"
" 0
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* With show function, fits just. */
opt
.
show
=
show_10
;
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
7
,
41
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 0123456789 0 (default: XXXXXXXXXX)
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* With show function, just too long. */
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
7
,
40
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 0123456789 0
\n
"
" (default: XXXXXXXXXX)
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* With maximal show function, fits just (we assume OPT_SHOW_LEN = 80. */
opt
.
show
=
show_max
;
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
7
,
114
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 0123456789 0 (default: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...)
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* With maximal show function, just too long. */
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
7
,
113
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 0123456789 0
\n
"
" (default: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...)
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* With added " <arg>". Fits, just. */
opt
.
show
=
NULL
;
opt
.
type
=
OPT_HASARG
;
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
13
,
25
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 <arg> 0123456789 0
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* With added " <arg>". Name doesn't quite fit. */
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
12
,
25
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 <arg>
\n
"
" 0123456789 0
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* With added " <arg>". Desc doesn't quite fit. */
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
13
,
24
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 <arg> 0123456789
\n
"
" 0
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* Empty description, with <arg> and default. Just fits. */
opt
.
show
=
show_10
;
opt
.
desc
=
""
;
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
13
,
35
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 <arg> (default: XXXXXXXXXX)
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
/* Empty description, with <arg> and default. Doesn't quite fit. */
opt
.
show
=
show_10
;
opt
.
desc
=
""
;
ret
=
add_desc
(
NULL
,
&
len
,
&
max
,
13
,
34
,
&
opt
);
ok1
(
len
<
max
);
ret
[
len
]
=
'\0'
;
ok1
(
strcmp
(
ret
,
"01234 <arg>
\n
"
" (default: XXXXXXXXXX)
\n
"
)
==
0
);
free
(
ret
);
len
=
max
=
0
;
return
exit_status
();
}
ccan/opt/test/run-consume_words.c
0 → 100644
View file @
3d45cf27
#include <ccan/tap/tap.h>
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
#include <ccan/opt/parse.c>
/* Test consume_words helper. */
int
main
(
int
argc
,
char
*
argv
[])
{
unsigned
int
start
,
len
;
plan_tests
(
13
);
/* Every line over width. */
len
=
consume_words
(
"hello world"
,
1
,
&
start
);
ok1
(
start
==
0
);
ok1
(
len
==
strlen
(
"hello"
));
len
=
consume_words
(
" world"
,
1
,
&
start
);
ok1
(
start
==
1
);
ok1
(
len
==
strlen
(
"world"
));
ok1
(
consume_words
(
""
,
1
,
&
start
)
==
0
);
/* Same with width where won't both fit. */
len
=
consume_words
(
"hello world"
,
5
,
&
start
);
ok1
(
start
==
0
);
ok1
(
len
==
strlen
(
"hello"
));
len
=
consume_words
(
" world"
,
5
,
&
start
);
ok1
(
start
==
1
);
ok1
(
len
==
strlen
(
"world"
));
ok1
(
consume_words
(
""
,
5
,
&
start
)
==
0
);
len
=
consume_words
(
"hello world"
,
11
,
&
start
);
ok1
(
start
==
0
);
ok1
(
len
==
strlen
(
"hello world"
));
ok1
(
consume_words
(
""
,
11
,
&
start
)
==
0
);
return
exit_status
();
}
ccan/opt/test/run-helpers.c
View file @
3d45cf27
...
...
@@ -1024,7 +1024,7 @@ int main(int argc, char *argv[])
}
ok1
(
strstr
(
output
,
"[args]"
));
ok1
(
strstr
(
output
,
argv
[
0
]));
ok1
(
strstr
(
output
,
"
[-a]
"
));
ok1
(
strstr
(
output
,
"
\n
-a
"
));
free
(
output
);
free
(
argv
);
output
=
NULL
;
...
...
ccan/opt/test/run-usage.c
View file @
3d45cf27
...
...
@@ -4,6 +4,15 @@
#include <stdlib.h>
#include <stdarg.h>
#include "utils.h"
/* Ensure width is sane. */
static
const
char
*
getenv_override
(
const
char
*
name
)
{
return
"100"
;
}
#define getenv getenv_override
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
...
...
ccan/opt/usage.c
View file @
3d45cf27
/* Licensed under GPLv3+ - see LICENSE file for details */
#include <ccan/opt/opt.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
...
...
@@ -9,26 +10,177 @@
/* We only use this for pointer comparisons. */
const
char
opt_hidden
[
1
];
static
unsigned
write_short_options
(
char
*
str
)
#define MIN_DESC_WIDTH 40
#define MIN_TOTAL_WIDTH 50
static
unsigned
int
get_columns
(
void
)
{
unsigned
int
i
,
num
=
0
;
const
char
*
p
;
struct
winsize
w
;
const
char
*
env
=
getenv
(
"COLUMNS"
);
w
.
ws_col
=
0
;
if
(
env
)
w
.
ws_col
=
atoi
(
env
);
if
(
!
w
.
ws_col
)
if
(
ioctl
(
0
,
TIOCGWINSZ
,
&
w
)
==
-
1
)
w
.
ws_col
=
0
;
if
(
!
w
.
ws_col
)
w
.
ws_col
=
80
;
return
w
.
ws_col
;
}
/* Return number of chars of words to put on this line.
* Prefix is set to number to skip at start, maxlen is max width, returns
* length (after prefix) to put on this line. */
static
size_t
consume_words
(
const
char
*
words
,
size_t
maxlen
,
size_t
*
prefix
)
{
size_t
oldlen
,
len
;
/* Swallow leading whitespace. */
*
prefix
=
strspn
(
words
,
" "
);
words
+=
*
prefix
;
for
(
p
=
first_sopt
(
&
i
);
p
;
p
=
next_sopt
(
p
,
&
i
))
{
if
(
opt_table
[
i
].
desc
!=
opt_hidden
)
str
[
num
++
]
=
*
p
;
/* Use at least one word, even if it takes us over maxlen. */
oldlen
=
len
=
strcspn
(
words
,
" "
);
while
(
len
<=
maxlen
)
{
oldlen
=
len
;
len
+=
strspn
(
words
+
len
,
" "
);
len
+=
strcspn
(
words
+
len
,
" "
);
if
(
len
==
oldlen
)
break
;
}
return
num
;
return
oldlen
;
}
static
char
*
add_str_len
(
char
*
base
,
size_t
*
len
,
size_t
*
max
,
const
char
*
str
,
size_t
slen
)
{
if
(
slen
>=
*
max
-
*
len
)
base
=
realloc
(
base
,
*
max
=
(
*
max
*
2
+
slen
+
1
));
memcpy
(
base
+
*
len
,
str
,
slen
);
*
len
+=
slen
;
return
base
;
}
#define OPT_SPACE_PAD " "
static
char
*
add_str
(
char
*
base
,
size_t
*
len
,
size_t
*
max
,
const
char
*
str
)
{
return
add_str_len
(
base
,
len
,
max
,
str
,
strlen
(
str
));
}
static
char
*
add_indent
(
char
*
base
,
size_t
*
len
,
size_t
*
max
,
size_t
indent
)
{
if
(
indent
>=
*
max
-
*
len
)
base
=
realloc
(
base
,
*
max
=
(
*
max
*
2
+
indent
+
1
));
memset
(
base
+
*
len
,
' '
,
indent
);
*
len
+=
indent
;
return
base
;
}
static
char
*
add_desc
(
char
*
base
,
size_t
*
len
,
size_t
*
max
,
unsigned
int
indent
,
unsigned
int
width
,
const
struct
opt_table
*
opt
)
{
size_t
off
,
prefix
,
l
;
const
char
*
p
;
bool
same_line
=
false
;
base
=
add_str
(
base
,
len
,
max
,
opt
->
names
);
off
=
strlen
(
opt
->
names
);
if
(
opt
->
type
==
OPT_HASARG
&&
!
strchr
(
opt
->
names
,
' '
)
&&
!
strchr
(
opt
->
names
,
'='
))
{
base
=
add_str
(
base
,
len
,
max
,
" <arg>"
);
off
+=
strlen
(
" <arg>"
);
}
/* Do we start description on next line? */
if
(
off
+
2
>
indent
)
{
base
=
add_str
(
base
,
len
,
max
,
"
\n
"
);
off
=
0
;
}
else
{
base
=
add_indent
(
base
,
len
,
max
,
indent
-
off
);
off
=
indent
;
same_line
=
true
;
}
/* Indent description. */
p
=
opt
->
desc
;
while
((
l
=
consume_words
(
p
,
width
-
indent
,
&
prefix
))
!=
0
)
{
if
(
!
same_line
)
base
=
add_indent
(
base
,
len
,
max
,
indent
);
p
+=
prefix
;
base
=
add_str_len
(
base
,
len
,
max
,
p
,
l
);
base
=
add_str
(
base
,
len
,
max
,
"
\n
"
);
off
=
indent
+
l
;
p
+=
l
;
same_line
=
false
;
}
/* Empty description? Make it match normal case. */
if
(
same_line
)
base
=
add_str
(
base
,
len
,
max
,
"
\n
"
);
if
(
opt
->
show
)
{
char
buf
[
OPT_SHOW_LEN
+
sizeof
(
"..."
)];
strcpy
(
buf
+
OPT_SHOW_LEN
,
"..."
);
opt
->
show
(
buf
,
opt
->
u
.
arg
);
/* If it doesn't fit on this line, indent. */
if
(
off
+
strlen
(
" (default: "
)
+
strlen
(
buf
)
+
strlen
(
")"
)
>
width
)
{
base
=
add_indent
(
base
,
len
,
max
,
indent
);
}
else
{
/* Remove \n. */
(
*
len
)
--
;
}
base
=
add_str
(
base
,
len
,
max
,
" (default: "
);
base
=
add_str
(
base
,
len
,
max
,
buf
);
base
=
add_str
(
base
,
len
,
max
,
")
\n
"
);
}
return
base
;
}
/* FIXME: Get all purdy. */
char
*
opt_usage
(
const
char
*
argv0
,
const
char
*
extra
)
{
unsigned
int
i
,
num
,
len
;
char
*
ret
,
*
p
;
unsigned
int
i
;
size_t
max
,
len
,
width
,
indent
;
char
*
ret
;
width
=
get_columns
();
if
(
width
<
MIN_TOTAL_WIDTH
)
width
=
MIN_TOTAL_WIDTH
;
/* Figure out longest option. */
indent
=
0
;
for
(
i
=
0
;
i
<
opt_count
;
i
++
)
{
size_t
l
;
if
(
opt_table
[
i
].
desc
==
opt_hidden
)
continue
;
if
(
opt_table
[
i
].
type
==
OPT_SUBTABLE
)
continue
;
l
=
strlen
(
opt_table
[
i
].
names
);
if
(
opt_table
[
i
].
type
==
OPT_HASARG
&&
!
strchr
(
opt_table
[
i
].
names
,
' '
)
&&
!
strchr
(
opt_table
[
i
].
names
,
'='
))
l
+=
strlen
(
" <arg>"
);
if
(
l
+
2
>
indent
)
indent
=
l
+
2
;
}
/* Now we know how much to indent */
if
(
indent
+
MIN_DESC_WIDTH
>
width
)
indent
=
width
-
MIN_DESC_WIDTH
;
len
=
max
=
0
;
ret
=
NULL
;
ret
=
add_str
(
ret
,
&
len
,
&
max
,
"Usage: "
);
ret
=
add_str
(
ret
,
&
len
,
&
max
,
argv0
);
/* Find usage message from among registered options if necessary. */
if
(
!
extra
)
{
extra
=
""
;
for
(
i
=
0
;
i
<
opt_count
;
i
++
)
{
...
...
@@ -39,71 +191,20 @@ char *opt_usage(const char *argv0, const char *extra)
}
}
}
/* An overestimate of our length. */
len
=
strlen
(
"Usage: %s "
)
+
strlen
(
argv0
)
+
strlen
(
"[-%.*s]"
)
+
opt_num_short
+
1
+
strlen
(
" "
)
+
strlen
(
extra
)
+
strlen
(
"
\n
"
);
for
(
i
=
0
;
i
<
opt_count
;
i
++
)
{
if
(
opt_table
[
i
].
type
==
OPT_SUBTABLE
)
{
len
+=
strlen
(
"
\n
"
)
+
strlen
(
opt_table
[
i
].
desc
)
+
strlen
(
":
\n
"
);
}
else
if
(
opt_table
[
i
].
desc
!=
opt_hidden
)
{
len
+=
strlen
(
opt_table
[
i
].
names
)
+
strlen
(
" <arg>"
);
len
+=
strlen
(
OPT_SPACE_PAD
)
+
strlen
(
opt_table
[
i
].
desc
)
+
1
;
if
(
opt_table
[
i
].
show
)
{
len
+=
strlen
(
"(default: %s)"
)
+
OPT_SHOW_LEN
+
sizeof
(
"..."
);
}
len
+=
strlen
(
"
\n
"
);
}
}
p
=
ret
=
malloc
(
len
);
p
+=
sprintf
(
p
,
"Usage: %s"
,
argv0
);
p
+=
sprintf
(
p
,
" [-"
);
num
=
write_short_options
(
p
);
if
(
num
)
{
p
+=
num
;
p
+=
sprintf
(
p
,
"]"
);
}
else
{
/* Remove start of single-entry options */
p
-=
3
;
}
if
(
extra
)
p
+=
sprintf
(
p
,
" %s"
,
extra
);
p
+=
sprintf
(
p
,
"
\n
"
);
ret
=
add_str
(
ret
,
&
len
,
&
max
,
" "
);
ret
=
add_str
(
ret
,
&
len
,
&
max
,
extra
);
ret
=
add_str
(
ret
,
&
len
,
&
max
,
"
\n
"
);
for
(
i
=
0
;
i
<
opt_count
;
i
++
)
{
if
(
opt_table
[
i
].
desc
==
opt_hidden
)
continue
;
if
(
opt_table
[
i
].
type
==
OPT_SUBTABLE
)
{
p
+=
sprintf
(
p
,
"%s:
\n
"
,
opt_table
[
i
].
desc
);
ret
=
add_str
(
ret
,
&
len
,
&
max
,
opt_table
[
i
].
desc
);
ret
=
add_str
(
ret
,
&
len
,
&
max
,
":
\n
"
);
continue
;
}
len
=
sprintf
(
p
,
"%s"
,
opt_table
[
i
].
names
);
if
(
opt_table
[
i
].
type
==
OPT_HASARG
&&
!
strchr
(
opt_table
[
i
].
names
,
' '
)
&&
!
strchr
(
opt_table
[
i
].
names
,
'='
))
len
+=
sprintf
(
p
+
len
,
" <arg>"
);
len
+=
sprintf
(
p
+
len
,
"%.*s"
,
len
<
strlen
(
OPT_SPACE_PAD
)
?
(
unsigned
)
strlen
(
OPT_SPACE_PAD
)
-
len
:
1
,
OPT_SPACE_PAD
);
len
+=
sprintf
(
p
+
len
,
"%s"
,
opt_table
[
i
].
desc
);
if
(
opt_table
[
i
].
show
)
{
char
buf
[
OPT_SHOW_LEN
+
sizeof
(
"..."
)];
strcpy
(
buf
+
OPT_SHOW_LEN
,
"..."
);
opt_table
[
i
].
show
(
buf
,
opt_table
[
i
].
u
.
arg
);
len
+=
sprintf
(
p
+
len
,
" (default: %s)"
,
buf
);
}
p
+=
len
;
p
+=
sprintf
(
p
,
"
\n
"
);
ret
=
add_desc
(
ret
,
&
len
,
&
max
,
indent
,
width
,
&
opt_table
[
i
]);
}
*
p
=
'\0'
;
ret
[
len
]
=
'\0'
;
return
ret
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment