diff --git a/.gitignore b/.gitignore index 5084276..91196fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/blueutil + /pkg/ build/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9e4407d --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +CFLAGS = -Wall -Wextra -Werror -framework IOBluetooth + +test: blueutil + ./test + +.PHONY: test diff --git a/blueutil.m b/blueutil.m index d111c02..d95bd39 100644 --- a/blueutil.m +++ b/blueutil.m @@ -10,7 +10,7 @@ #define VERSION "1.0.0" -#import +#import // private methods int IOBluetoothPreferencesAvailable(); @@ -22,92 +22,118 @@ void IOBluetoothPreferenceSetDiscoverableState(int state); // dry -int BTSetParamState(int state, int (*getter)(), void (*setter)(int)) { - if (state == getter()) { - return EXIT_SUCCESS; - } else { - setter(state); - - usleep(1000000); // Just wait, checking getter even in 10 seconds gives old result - return EXIT_SUCCESS; +int BTSetParamState(int state, int (*getter)(), void (*setter)(int), char *name) { + if (state == getter()) return true; + + setter(state); + + for (int i = 0; i < 101; i++) { + if (i) usleep(100000); + if (state == getter()) return true; } + + fprintf(stderr, "Failed to switch bluetooth %s %s in 10 seconds\n", name, state ? "on" : "off"); + return false; } // short names typedef int (*getterFunc)(); -typedef int (*setterFunc)(int); +typedef bool (*setterFunc)(int); #define BTAvaliable IOBluetoothPreferencesAvailable #define BTPowerState IOBluetoothPreferenceGetControllerPowerState -int BTSetPowerState(int state) { - return BTSetParamState(state, BTPowerState, IOBluetoothPreferenceSetControllerPowerState); +bool BTSetPowerState(int state) { + return BTSetParamState(state, BTPowerState, IOBluetoothPreferenceSetControllerPowerState, "power"); } #define BTDiscoverableState IOBluetoothPreferenceGetDiscoverableState -int BTSetDiscoverableState(int state) { - return BTSetParamState(state, BTDiscoverableState, IOBluetoothPreferenceSetDiscoverableState); +bool BTSetDiscoverableState(int state) { + return BTSetParamState(state, BTDiscoverableState, IOBluetoothPreferenceSetDiscoverableState, "discoverable state"); } -void printHelp() { - fprintf(stderr, - "blueutil v%s\n\n" \ - "blueutil - show state\n" \ - "blueutil power|discoverable - show state 1 or 0\n" \ - "blueutil power|discoverable 1|0 - set state\n", VERSION); +#define io_puts(io, string) fputs (string"\n", io) + +void printHelp(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, ""); + 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, ""); + 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"); } -int main(int argc, const char * argv[]) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - int result = EXIT_FAILURE; +#define is_abbr_arg(name, arg) (strncmp((name), (arg), strlen(arg) || 1) == 0) +int main(int argc, const char * argv[]) { if (!BTAvaliable()) { - fprintf(stderr, "Error: Bluetooth not available!\n"); - } else { - switch (argc) { - case 1: { - printf("Power: %d\nDiscoverable: %d\n", BTPowerState(), BTDiscoverableState()); - result = EXIT_SUCCESS; - break; + 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])) { + io_puts(stdout, VERSION); + return EXIT_SUCCESS; + } + if (is_abbr_arg("status", argv[1])) { + printf("Status: %s\n", BTPowerState() ? "on" : "off"); + 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); + return EXIT_FAILURE; } - case 2: - case 3: { - getterFunc getter = NULL; - setterFunc setter = NULL; - - if (strcmp("power", argv[1]) == 0) { - getter = BTPowerState; - setter = BTSetPowerState; - } else if (strcmp("discoverable", argv[1]) == 0) { - getter = BTDiscoverableState; - setter = BTSetDiscoverableState; - } else { - printHelp(); - break; - } - if (argc == 2) { - printf("%d\n", getter()); - result = EXIT_SUCCESS; + 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; } else { - if (strcmp("1", argv[2]) == 0) { - result = setter(1); - } else if (strcmp("0", argv[2]) == 0) { - result = setter(0); - } else { - printHelp(); - break; - } + printHelp(stderr); + return EXIT_FAILURE; } - break; - } - default: { - printHelp(); - break; } } + default: { + printHelp(stderr); + return EXIT_FAILURE; + } } - - [pool release]; - return result; } diff --git a/blueutil.xcodeproj/project.pbxproj b/blueutil.xcodeproj/project.pbxproj index 664e28d..d3358e3 100644 --- a/blueutil.xcodeproj/project.pbxproj +++ b/blueutil.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 8DD76F9A0486AA7600D96B5E /* blueutil.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* blueutil.m */; settings = {ATTRIBUTES = (); }; }; - 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; B2A6DE1312F4624400C5007F /* IOBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2A6DE1212F4624400C5007F /* IOBluetooth.framework */; }; /* End PBXBuildFile section */ @@ -26,7 +25,6 @@ /* Begin PBXFileReference section */ 08FB7796FE84155DC02AAC07 /* blueutil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = blueutil.m; sourceTree = ""; }; - 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 32A70AAB03705E1F00C91783 /* blueutil_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blueutil_Prefix.pch; sourceTree = ""; }; 8DD76FA10486AA7600D96B5E /* blueutil */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = blueutil; sourceTree = BUILT_PRODUCTS_DIR; }; B2A6DE1212F4624400C5007F /* IOBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOBluetooth.framework; path = System/Library/Frameworks/IOBluetooth.framework; sourceTree = SDKROOT; }; @@ -37,7 +35,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, B2A6DE1312F4624400C5007F /* IOBluetooth.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -67,7 +64,6 @@ 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { isa = PBXGroup; children = ( - 08FB779EFE84155DC02AAC07 /* Foundation.framework */, B2A6DE1212F4624400C5007F /* IOBluetooth.framework */, ); name = "External Frameworks and Libraries"; @@ -170,7 +166,6 @@ GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; PREBINDING = NO; - SDKROOT = macosx10.6; }; name = Debug; }; @@ -182,7 +177,6 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - SDKROOT = macosx10.6; }; name = Release; }; diff --git a/blueutil_Prefix.pch b/blueutil_Prefix.pch index 9816782..6a9a17c 100644 --- a/blueutil_Prefix.pch +++ b/blueutil_Prefix.pch @@ -3,5 +3,5 @@ // #ifdef __OBJC__ - #import + #import #endif diff --git a/test b/test new file mode 100755 index 0000000..509ca6f --- /dev/null +++ b/test @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +blueutil=./blueutil +errors= + +status=$($blueutil) +trap "{ + $blueutil power $($blueutil power) + $blueutil discoverable $($blueutil discoverable) + echo + if [[ -n \$errors ]]; then + echo -n \"\$errors\" + exit 1 + fi +}" EXIT + +ANSI_BOLD=$(tput bold 2> /dev/null) +ANSI_RED=$(tput setaf 1 2> /dev/null) +ANSI_GREEN=$(tput setaf 2 2> /dev/null) +ANSI_RESET=$(tput sgr0 2> /dev/null) + +success() { + printf "$ANSI_GREEN"."$ANSI_RESET" +} + +failure() { + printf "$ANSI_RED"F"$ANSI_RESET" + errors+="$@"$'\n' +} + +assert_eq() { + [[ $1 == $2 ]] && success || failure "$ANSI_BOLD""$BASH_LINENO""$ANSI_RESET: Expected \"$1\" to eq \"$2\"" +} + +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 power 0 +assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0' + +# Power abbreviation +$blueutil p 1 +assert_eq "$($blueutil)" $'Power: 1\nDiscoverable: 0' + +$blueutil p 0 +assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0' + +# Discoverable +$blueutil discoverable 1 +assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 1' + +$blueutil discoverable 0 +assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0' + +# Discoverable abbreviation +$blueutil d 1 +assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 1' + +$blueutil d 0 +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 +assert_eq "$($blueutil)" $'Power: 1\nDiscoverable: 0' + +# Off +$blueutil off +assert_eq "$($blueutil)" $'Power: 0\nDiscoverable: 0' + +# 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]+$'