Skip to content

Commit

Permalink
change arguments specification, resolve #7
Browse files Browse the repository at this point in the history
  • Loading branch information
toy committed Feb 18, 2018
1 parent 09f5007 commit 5cbc75b
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 127 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
get/set bluetooth power and discoverable state

```
blueutil h[elp] - this help
blueutil v[ersion] - show version
Usage:
blueutil [options]
blueutil - show state
blueutil p[ower]|d[iscoverable] - show state 1 or 0
blueutil p[ower]|d[iscoverable] 1|0 - set state
Without options outputs current state
Also original style arguments:
blueutil s[tatus] - show status
blueutil on - power on
blueutil off - power off
-p, --power output power state as 1 or 0
-p, --power 1|on|0|off set power state
-d, --discoverable output discoverable state as 1 or 0
-d, --discoverable 1|on|0|off set discoverable state
-h, --help this help
-v, --version show version
```

Uses private API from IOBluetooth framework (i.e. IOBluetoothPreference*()).
Expand Down
177 changes: 112 additions & 65 deletions blueutil.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

#import <IOBluetooth/IOBluetooth.h>

#include <getopt.h>

// private methods
int IOBluetoothPreferencesAvailable();

Expand Down Expand Up @@ -56,90 +58,135 @@ bool BTSetDiscoverableState(int state) {

#define io_puts(io, string) fputs (string"\n", io)

void printHelp(FILE *io) {
void usage(FILE *io) {
io_puts(io, "blueutil v"VERSION);
io_puts(io, "");
io_puts(io, "blueutil h[elp] - this help");
io_puts(io, "blueutil v[ersion] - show version");
io_puts(io, "Usage:");
io_puts(io, " blueutil [options]");
io_puts(io, "");
io_puts(io, "Without options outputs current state");
io_puts(io, "");
io_puts(io, "blueutil - show state");
io_puts(io, "blueutil p[ower]|d[iscoverable] - show state 1 or 0");
io_puts(io, "blueutil p[ower]|d[iscoverable] 1|0 - set state");
io_puts(io, " -p, --power output power state as 1 or 0");
io_puts(io, " -p, --power 1|on|0|off set power state");
io_puts(io, " -d, --discoverable output discoverable state as 1 or 0");
io_puts(io, " -d, --discoverable 1|on|0|off set discoverable state");
io_puts(io, "");
io_puts(io, "Also original style arguments:");
io_puts(io, "blueutil s[tatus] - show status");
io_puts(io, "blueutil on - power on");
io_puts(io, "blueutil off - power off");
io_puts(io, " -h, --help this help");
io_puts(io, " -v, --version show version");
}

static inline bool is_abbr_arg(const char* name, const char* arg) {
size_t length = strlen(arg);
if (length < 1) length = 1;
return strncmp(name, arg, length) == 0;
// getopt_long doesn't consume optional argument separated by space
// https://stackoverflow.com/a/32575314
void extend_optarg(int argc, char *argv[]) {
if (
!optarg &&
optind < argc &&
NULL != argv[optind] &&
'-' != argv[optind][0]
) {
optarg = argv[optind++];
}
}

int main(int argc, const char * argv[]) {
bool parse_state_arg(char *str, int *state) {
if (
0 == strcasecmp(str, "1") ||
0 == strcasecmp(str, "on")
) {
if (state) *state = 1;
return true;
}

if (
0 == strcasecmp(optarg, "0") ||
0 == strcasecmp(optarg, "off")
) {
if (state) *state = 0;
return true;
}

return false;
}

int main(int argc, char *argv[]) {
if (!BTAvaliable()) {
io_puts(stderr, "Error: Bluetooth not available!");
return EXIT_FAILURE;
}
switch (argc) {
case 1: {
printf("Power: %d\nDiscoverable: %d\n", BTPowerState(), BTDiscoverableState());
return EXIT_SUCCESS;
}
case 2: {
if (is_abbr_arg("help", argv[1])) {
printHelp(stdout);
return EXIT_SUCCESS;
}
if (is_abbr_arg("version", argv[1])) {

if (argc == 1) {
printf("Power: %d\nDiscoverable: %d\n", BTPowerState(), BTDiscoverableState());
return EXIT_SUCCESS;
}

const char* optstring = "p::d::hv";
static struct option long_options[] = {
{"power", optional_argument, NULL, 'p'},
{"discoverable", optional_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0}
};

int ch;
while ((ch = getopt_long(argc, argv, optstring, long_options, NULL)) != -1) {
switch (ch) {
case 'p':
case 'd':
extend_optarg(argc, argv);

if (optarg && !parse_state_arg(optarg, NULL)) {
fprintf(stderr, "Unexpected value: %s", optarg);
return EXIT_FAILURE;
}

break;
case 'v':
io_puts(stdout, VERSION);
return EXIT_SUCCESS;
}
if (is_abbr_arg("status", argv[1])) {
printf("Status: %s\n", BTPowerState() ? "on" : "off");
case 'h':
usage(stdout);
return EXIT_SUCCESS;
}
if (strcmp("on", argv[1]) == 0) {
return BTSetPowerState(1) ? EXIT_SUCCESS : EXIT_FAILURE;
}
if (strcmp("off", argv[1]) == 0) {
return BTSetPowerState(0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
}
case 3: {
getterFunc getter = NULL;
setterFunc setter = NULL;

if (is_abbr_arg("power", argv[1])) {
getter = BTPowerState;
setter = BTSetPowerState;
} else if (is_abbr_arg("discoverable", argv[1])) {
getter = BTDiscoverableState;
setter = BTSetDiscoverableState;
} else {
printHelp(stderr);
default:
usage(stderr);
return EXIT_FAILURE;
}
}
}

if (argc == 2) {
printf("%d\n", getter());
return EXIT_SUCCESS;
} else {
if (strcmp("1", argv[2]) == 0) {
return setter(1) ? EXIT_SUCCESS : EXIT_FAILURE;
} else if (strcmp("0", argv[2]) == 0) {
return setter(0) ? EXIT_SUCCESS : EXIT_FAILURE;
if (optind < argc) {
fprintf(stderr, "Unexpected arguments: %s", argv[optind++]);
while (optind < argc) {
fprintf(stderr, ", %s", argv[optind++]);
}
fprintf(stderr, "\n");
return EXIT_FAILURE;
}

optind = 1;
while ((ch = getopt_long(argc, argv, optstring, long_options, NULL)) != -1) {
switch (ch) {
case 'p':
case 'd':
extend_optarg(argc, argv);

if (optarg) {
setterFunc setter = ch == 'p' ? BTSetPowerState : BTSetDiscoverableState;

int state;
parse_state_arg(optarg, &state);

if (!setter(state)) {
return EXIT_FAILURE;
}
} else {
printHelp(stderr);
return EXIT_FAILURE;
getterFunc getter = ch == 'p' ? BTPowerState : BTDiscoverableState;

printf("%d\n", getter());
}
}
}
default: {
printHelp(stderr);
return EXIT_FAILURE;

break;
}
}

return EXIT_SUCCESS;
}
96 changes: 43 additions & 53 deletions test
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ errors=

status=$($blueutil)
trap "{
$blueutil power $($blueutil power)
$blueutil discoverable $($blueutil discoverable)
$blueutil -p$($blueutil -p) -d$($blueutil -d)
echo
if [[ -n \$errors ]]; then
echo -n \"\$errors\"
Expand Down Expand Up @@ -47,70 +46,61 @@ assert_match() {
[[ $1 =~ $2 ]] && success || failure "$ANSI_BOLD""$BASH_LINENO""$ANSI_RESET: Expected \"$1\" to match \"$2\""
}

# Init
$blueutil power 1
$blueutil discoverable 1
assert_eq "$($blueutil)" $'Power: 1\nDiscoverable: 1'
$blueutil power 0
$blueutil discoverable 0
assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0'

# Power
$blueutil power 1
assert_eq "$($blueutil)" $'Power: 1\nDiscoverable: 0'
$blueutil -p1
assert_eq "$($blueutil)" *'Power: 1'*

$blueutil power 0
assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0'
$blueutil -p 0
assert_eq "$($blueutil)" *'Power: 0'*

# Power abbreviation
$blueutil p 1
assert_eq "$($blueutil)" $'Power: 1\nDiscoverable: 0'
$blueutil --power=1
assert_eq "$($blueutil)" *'Power: 1'*
assert_eq "$($blueutil -p)" '1'
assert_eq "$($blueutil --pow)" '1'
assert_eq "$($blueutil --power)" '1'

$blueutil p 0
assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0'
$blueutil --pow 0
assert_eq "$($blueutil)" *'Power: 0'*
assert_eq "$($blueutil -p)" '0'
assert_eq "$($blueutil --pow)" '0'
assert_eq "$($blueutil --power)" '0'

# Discoverable
$blueutil discoverable 1
assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 1'

$blueutil discoverable 0
assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0'
$blueutil -d1
assert_eq "$($blueutil)" *'Discoverable: 1'*

$blueutil -d 0
assert_eq "$($blueutil)" *'Discoverable: 0'*

$blueutil --discoverable=1
assert_eq "$($blueutil)" *'Discoverable: 1'*
assert_eq "$($blueutil -d)" '1'
assert_eq "$($blueutil --dis)" '1'
assert_eq "$($blueutil --discoverable)" '1'

$blueutil --dis 0
assert_eq "$($blueutil)" *'Discoverable: 0'*
assert_eq "$($blueutil -d)" '0'
assert_eq "$($blueutil --dis)" '0'
assert_eq "$($blueutil --discoverable)" '0'

# Combined
$blueutil -p1 -d1
assert_eq "$($blueutil)" $'Power: 1\nDiscoverable: 1'

# Discoverable abbreviation
$blueutil d 1
$blueutil -p0 -d1
assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 1'

$blueutil d 0
assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0'

# Wrong abbreviation
$blueutil pawer 1 &> /dev/null
assert_eq "$?" 1
assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0'

$blueutil discoverabla 1 &> /dev/null
assert_eq "$?" 1
assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0'

# Status
assert_eq "$($blueutil status)" 'Status: off'
$blueutil power 1
assert_eq "$($blueutil status)" 'Status: on'
$blueutil power 0
assert_eq "$($blueutil status)" 'Status: off'

# On
$blueutil on
$blueutil -pon -dOff
assert_eq "$($blueutil)" $'Power: 1\nDiscoverable: 0'

# Off
$blueutil off
$blueutil -pOFF -doFF
assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0'

# Help
assert_eq "$($blueutil help)" *'- this help'*
assert_eq "$($blueutil h)" *'- this help'*
assert_eq "$($blueutil --help)" *'this help'*
assert_eq "$($blueutil -h)" *'this help'*

# Version
assert_match "$($blueutil version)" '^[0-9]+\.[0-9]+\.[0-9]+$'
assert_match "$($blueutil v)" '^[0-9]+\.[0-9]+\.[0-9]+$'
assert_match "$($blueutil --version)" '^[0-9]+\.[0-9]+\.[0-9]+$'
assert_match "$($blueutil -v)" '^[0-9]+\.[0-9]+\.[0-9]+$'

0 comments on commit 5cbc75b

Please sign in to comment.