├── ClevoControl.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── datasone.xcuserdatad │ └── xcschemes │ ├── ClevoControl.xcscheme │ └── xcschememanagement.plist ├── ClevoControl ├── ClevoControl.cpp ├── ClevoControl.hpp └── Info.plist ├── ClevoKBFanControl ├── args.hxx └── main.cpp ├── ECCtrl.h ├── LICENSE └── README.md /ClevoControl.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 11A652441F39D78A002E8088 /* ClevoControl.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 11A652431F39D78A002E8088 /* ClevoControl.hpp */; }; 11 | 11A652461F39D78A002E8088 /* ClevoControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 11A652451F39D78A002E8088 /* ClevoControl.cpp */; }; 12 | 11A652541F3A1014002E8088 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 11A652531F3A1014002E8088 /* main.cpp */; }; 13 | 11A6525A1F3A1E18002E8088 /* ECCtrl.h in Headers */ = {isa = PBXBuildFile; fileRef = 11A652591F3A1E18002E8088 /* ECCtrl.h */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | 11A6524F1F3A1014002E8088 /* CopyFiles */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = /usr/share/man/man1/; 21 | dstSubfolderSpec = 0; 22 | files = ( 23 | ); 24 | runOnlyForDeploymentPostprocessing = 1; 25 | }; 26 | /* End PBXCopyFilesBuildPhase section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 11A652401F39D78A002E8088 /* ClevoControl.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ClevoControl.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 11A652431F39D78A002E8088 /* ClevoControl.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ClevoControl.hpp; sourceTree = ""; }; 31 | 11A652451F39D78A002E8088 /* ClevoControl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ClevoControl.cpp; sourceTree = ""; }; 32 | 11A652471F39D78A002E8088 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 11A652511F3A1014002E8088 /* ClevoKBFanControl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ClevoKBFanControl; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 11A652531F3A1014002E8088 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 35 | 11A652581F3A15E6002E8088 /* args.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = args.hxx; sourceTree = ""; }; 36 | 11A652591F3A1E18002E8088 /* ECCtrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ECCtrl.h; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 11A6523C1F39D78A002E8088 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | 11A6524E1F3A1014002E8088 /* Frameworks */ = { 48 | isa = PBXFrameworksBuildPhase; 49 | buildActionMask = 2147483647; 50 | files = ( 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 11A652361F39D78A002E8088 = { 58 | isa = PBXGroup; 59 | children = ( 60 | 11A652591F3A1E18002E8088 /* ECCtrl.h */, 61 | 11A652421F39D78A002E8088 /* ClevoControl */, 62 | 11A652521F3A1014002E8088 /* ClevoKBFanControl */, 63 | 11A652411F39D78A002E8088 /* Products */, 64 | ); 65 | sourceTree = ""; 66 | }; 67 | 11A652411F39D78A002E8088 /* Products */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 11A652401F39D78A002E8088 /* ClevoControl.kext */, 71 | 11A652511F3A1014002E8088 /* ClevoKBFanControl */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 11A652421F39D78A002E8088 /* ClevoControl */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 11A652431F39D78A002E8088 /* ClevoControl.hpp */, 80 | 11A652451F39D78A002E8088 /* ClevoControl.cpp */, 81 | 11A652471F39D78A002E8088 /* Info.plist */, 82 | ); 83 | path = ClevoControl; 84 | sourceTree = ""; 85 | }; 86 | 11A652521F3A1014002E8088 /* ClevoKBFanControl */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 11A652531F3A1014002E8088 /* main.cpp */, 90 | 11A652581F3A15E6002E8088 /* args.hxx */, 91 | ); 92 | path = ClevoKBFanControl; 93 | sourceTree = ""; 94 | }; 95 | /* End PBXGroup section */ 96 | 97 | /* Begin PBXHeadersBuildPhase section */ 98 | 11A6523D1F39D78A002E8088 /* Headers */ = { 99 | isa = PBXHeadersBuildPhase; 100 | buildActionMask = 2147483647; 101 | files = ( 102 | 11A652441F39D78A002E8088 /* ClevoControl.hpp in Headers */, 103 | 11A6525A1F3A1E18002E8088 /* ECCtrl.h in Headers */, 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | /* End PBXHeadersBuildPhase section */ 108 | 109 | /* Begin PBXNativeTarget section */ 110 | 11A6523F1F39D78A002E8088 /* ClevoControl */ = { 111 | isa = PBXNativeTarget; 112 | buildConfigurationList = 11A6524A1F39D78A002E8088 /* Build configuration list for PBXNativeTarget "ClevoControl" */; 113 | buildPhases = ( 114 | 11A6523B1F39D78A002E8088 /* Sources */, 115 | 11A6523C1F39D78A002E8088 /* Frameworks */, 116 | 11A6523D1F39D78A002E8088 /* Headers */, 117 | 11A6523E1F39D78A002E8088 /* Resources */, 118 | ); 119 | buildRules = ( 120 | ); 121 | dependencies = ( 122 | ); 123 | name = ClevoControl; 124 | productName = ClevoControl; 125 | productReference = 11A652401F39D78A002E8088 /* ClevoControl.kext */; 126 | productType = "com.apple.product-type.kernel-extension"; 127 | }; 128 | 11A652501F3A1014002E8088 /* ClevoKBFanControl */ = { 129 | isa = PBXNativeTarget; 130 | buildConfigurationList = 11A652571F3A1014002E8088 /* Build configuration list for PBXNativeTarget "ClevoKBFanControl" */; 131 | buildPhases = ( 132 | 11A6524D1F3A1014002E8088 /* Sources */, 133 | 11A6524E1F3A1014002E8088 /* Frameworks */, 134 | 11A6524F1F3A1014002E8088 /* CopyFiles */, 135 | ); 136 | buildRules = ( 137 | ); 138 | dependencies = ( 139 | ); 140 | name = ClevoKBFanControl; 141 | productName = ClevoKBFanControl; 142 | productReference = 11A652511F3A1014002E8088 /* ClevoKBFanControl */; 143 | productType = "com.apple.product-type.tool"; 144 | }; 145 | /* End PBXNativeTarget section */ 146 | 147 | /* Begin PBXProject section */ 148 | 11A652371F39D78A002E8088 /* Project object */ = { 149 | isa = PBXProject; 150 | attributes = { 151 | LastUpgradeCheck = 0830; 152 | ORGANIZATIONNAME = datasone; 153 | TargetAttributes = { 154 | 11A6523F1F39D78A002E8088 = { 155 | CreatedOnToolsVersion = 8.3.3; 156 | ProvisioningStyle = Automatic; 157 | }; 158 | 11A652501F3A1014002E8088 = { 159 | CreatedOnToolsVersion = 8.3.3; 160 | ProvisioningStyle = Automatic; 161 | }; 162 | }; 163 | }; 164 | buildConfigurationList = 11A6523A1F39D78A002E8088 /* Build configuration list for PBXProject "ClevoControl" */; 165 | compatibilityVersion = "Xcode 3.2"; 166 | developmentRegion = English; 167 | hasScannedForEncodings = 0; 168 | knownRegions = ( 169 | en, 170 | ); 171 | mainGroup = 11A652361F39D78A002E8088; 172 | productRefGroup = 11A652411F39D78A002E8088 /* Products */; 173 | projectDirPath = ""; 174 | projectRoot = ""; 175 | targets = ( 176 | 11A6523F1F39D78A002E8088 /* ClevoControl */, 177 | 11A652501F3A1014002E8088 /* ClevoKBFanControl */, 178 | ); 179 | }; 180 | /* End PBXProject section */ 181 | 182 | /* Begin PBXResourcesBuildPhase section */ 183 | 11A6523E1F39D78A002E8088 /* Resources */ = { 184 | isa = PBXResourcesBuildPhase; 185 | buildActionMask = 2147483647; 186 | files = ( 187 | ); 188 | runOnlyForDeploymentPostprocessing = 0; 189 | }; 190 | /* End PBXResourcesBuildPhase section */ 191 | 192 | /* Begin PBXSourcesBuildPhase section */ 193 | 11A6523B1F39D78A002E8088 /* Sources */ = { 194 | isa = PBXSourcesBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | 11A652461F39D78A002E8088 /* ClevoControl.cpp in Sources */, 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | }; 201 | 11A6524D1F3A1014002E8088 /* Sources */ = { 202 | isa = PBXSourcesBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | 11A652541F3A1014002E8088 /* main.cpp in Sources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXSourcesBuildPhase section */ 210 | 211 | /* Begin XCBuildConfiguration section */ 212 | 11A652481F39D78A002E8088 /* Debug */ = { 213 | isa = XCBuildConfiguration; 214 | buildSettings = { 215 | ALWAYS_SEARCH_USER_PATHS = NO; 216 | CLANG_ANALYZER_NONNULL = YES; 217 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 218 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 219 | CLANG_CXX_LIBRARY = "libc++"; 220 | CLANG_ENABLE_MODULES = YES; 221 | CLANG_ENABLE_OBJC_ARC = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_CONSTANT_CONVERSION = YES; 224 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 225 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 226 | CLANG_WARN_EMPTY_BODY = YES; 227 | CLANG_WARN_ENUM_CONVERSION = YES; 228 | CLANG_WARN_INFINITE_RECURSION = YES; 229 | CLANG_WARN_INT_CONVERSION = YES; 230 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 231 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 232 | CLANG_WARN_UNREACHABLE_CODE = YES; 233 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 234 | CODE_SIGN_IDENTITY = "-"; 235 | COPY_PHASE_STRIP = NO; 236 | DEBUG_INFORMATION_FORMAT = dwarf; 237 | ENABLE_STRICT_OBJC_MSGSEND = YES; 238 | ENABLE_TESTABILITY = YES; 239 | GCC_C_LANGUAGE_STANDARD = gnu99; 240 | GCC_DYNAMIC_NO_PIC = NO; 241 | GCC_NO_COMMON_BLOCKS = YES; 242 | GCC_OPTIMIZATION_LEVEL = 0; 243 | GCC_PREPROCESSOR_DEFINITIONS = ( 244 | "DEBUG=1", 245 | "$(inherited)", 246 | ); 247 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 248 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 249 | GCC_WARN_UNDECLARED_SELECTOR = YES; 250 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 251 | GCC_WARN_UNUSED_FUNCTION = YES; 252 | GCC_WARN_UNUSED_VARIABLE = YES; 253 | MACOSX_DEPLOYMENT_TARGET = 10.13; 254 | MTL_ENABLE_DEBUG_INFO = YES; 255 | ONLY_ACTIVE_ARCH = YES; 256 | SDKROOT = macosx; 257 | }; 258 | name = Debug; 259 | }; 260 | 11A652491F39D78A002E8088 /* Release */ = { 261 | isa = XCBuildConfiguration; 262 | buildSettings = { 263 | ALWAYS_SEARCH_USER_PATHS = NO; 264 | CLANG_ANALYZER_NONNULL = YES; 265 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 266 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 267 | CLANG_CXX_LIBRARY = "libc++"; 268 | CLANG_ENABLE_MODULES = YES; 269 | CLANG_ENABLE_OBJC_ARC = YES; 270 | CLANG_WARN_BOOL_CONVERSION = YES; 271 | CLANG_WARN_CONSTANT_CONVERSION = YES; 272 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 273 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 274 | CLANG_WARN_EMPTY_BODY = YES; 275 | CLANG_WARN_ENUM_CONVERSION = YES; 276 | CLANG_WARN_INFINITE_RECURSION = YES; 277 | CLANG_WARN_INT_CONVERSION = YES; 278 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 279 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 280 | CLANG_WARN_UNREACHABLE_CODE = YES; 281 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 282 | CODE_SIGN_IDENTITY = "-"; 283 | COPY_PHASE_STRIP = NO; 284 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 285 | ENABLE_NS_ASSERTIONS = NO; 286 | ENABLE_STRICT_OBJC_MSGSEND = YES; 287 | GCC_C_LANGUAGE_STANDARD = gnu99; 288 | GCC_NO_COMMON_BLOCKS = YES; 289 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 290 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 291 | GCC_WARN_UNDECLARED_SELECTOR = YES; 292 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 293 | GCC_WARN_UNUSED_FUNCTION = YES; 294 | GCC_WARN_UNUSED_VARIABLE = YES; 295 | MACOSX_DEPLOYMENT_TARGET = 10.13; 296 | MTL_ENABLE_DEBUG_INFO = NO; 297 | SDKROOT = macosx; 298 | }; 299 | name = Release; 300 | }; 301 | 11A6524B1F39D78A002E8088 /* Debug */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | COMBINE_HIDPI_IMAGES = YES; 305 | CURRENT_PROJECT_VERSION = 1.0.0d1; 306 | INFOPLIST_FILE = ClevoControl/Info.plist; 307 | MACOSX_DEPLOYMENT_TARGET = 10.12; 308 | MODULE_NAME = moe.datasone.ClevoControl; 309 | MODULE_VERSION = 1.0.0d1; 310 | PRODUCT_BUNDLE_IDENTIFIER = moe.datasone.ClevoControl; 311 | PRODUCT_NAME = "$(TARGET_NAME)"; 312 | WRAPPER_EXTENSION = kext; 313 | }; 314 | name = Debug; 315 | }; 316 | 11A6524C1F39D78A002E8088 /* Release */ = { 317 | isa = XCBuildConfiguration; 318 | buildSettings = { 319 | COMBINE_HIDPI_IMAGES = YES; 320 | CURRENT_PROJECT_VERSION = 1.0.0d1; 321 | INFOPLIST_FILE = ClevoControl/Info.plist; 322 | MACOSX_DEPLOYMENT_TARGET = 10.12; 323 | MODULE_NAME = moe.datasone.ClevoControl; 324 | MODULE_VERSION = 1.0.0d1; 325 | PRODUCT_BUNDLE_IDENTIFIER = moe.datasone.ClevoControl; 326 | PRODUCT_NAME = "$(TARGET_NAME)"; 327 | WRAPPER_EXTENSION = kext; 328 | }; 329 | name = Release; 330 | }; 331 | 11A652551F3A1014002E8088 /* Debug */ = { 332 | isa = XCBuildConfiguration; 333 | buildSettings = { 334 | HEADER_SEARCH_PATHS = ""; 335 | PRODUCT_NAME = "$(TARGET_NAME)"; 336 | }; 337 | name = Debug; 338 | }; 339 | 11A652561F3A1014002E8088 /* Release */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | HEADER_SEARCH_PATHS = ""; 343 | PRODUCT_NAME = "$(TARGET_NAME)"; 344 | }; 345 | name = Release; 346 | }; 347 | /* End XCBuildConfiguration section */ 348 | 349 | /* Begin XCConfigurationList section */ 350 | 11A6523A1F39D78A002E8088 /* Build configuration list for PBXProject "ClevoControl" */ = { 351 | isa = XCConfigurationList; 352 | buildConfigurations = ( 353 | 11A652481F39D78A002E8088 /* Debug */, 354 | 11A652491F39D78A002E8088 /* Release */, 355 | ); 356 | defaultConfigurationIsVisible = 0; 357 | defaultConfigurationName = Release; 358 | }; 359 | 11A6524A1F39D78A002E8088 /* Build configuration list for PBXNativeTarget "ClevoControl" */ = { 360 | isa = XCConfigurationList; 361 | buildConfigurations = ( 362 | 11A6524B1F39D78A002E8088 /* Debug */, 363 | 11A6524C1F39D78A002E8088 /* Release */, 364 | ); 365 | defaultConfigurationIsVisible = 0; 366 | defaultConfigurationName = Release; 367 | }; 368 | 11A652571F3A1014002E8088 /* Build configuration list for PBXNativeTarget "ClevoKBFanControl" */ = { 369 | isa = XCConfigurationList; 370 | buildConfigurations = ( 371 | 11A652551F3A1014002E8088 /* Debug */, 372 | 11A652561F3A1014002E8088 /* Release */, 373 | ); 374 | defaultConfigurationIsVisible = 0; 375 | defaultConfigurationName = Release; 376 | }; 377 | /* End XCConfigurationList section */ 378 | }; 379 | rootObject = 11A652371F39D78A002E8088 /* Project object */; 380 | } 381 | -------------------------------------------------------------------------------- /ClevoControl.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ClevoControl.xcodeproj/xcuserdata/datasone.xcuserdatad/xcschemes/ClevoControl.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ClevoControl.xcodeproj/xcuserdata/datasone.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ClevoControl.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | ClevoKBFanControl.xcscheme 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 11A6523F1F39D78A002E8088 21 | 22 | primary 23 | 24 | 25 | 11A652501F3A1014002E8088 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ClevoControl/ClevoControl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ClevoControl.hpp" 5 | #include "ECCtrl.h" 6 | 7 | OSDefineMetaClassAndStructors(moe_datasone_ClevoControl, IOService) 8 | 9 | bool ClevoControl::init(OSDictionary *dict) 10 | { 11 | bool result = IOService::init(dict); 12 | device = nullptr; 13 | return result; 14 | } 15 | 16 | IOService *ClevoControl::probe(IOService *provider, SInt32 *score) 17 | { 18 | return IOService::probe(provider, score); 19 | } 20 | 21 | errno_t EPHandleSet(kern_ctl_ref ctlref, unsigned int unit, void *userdata, int opt, void *data, size_t len) 22 | { 23 | return 0; 24 | } 25 | 26 | errno_t EPHandleGet(kern_ctl_ref ctlref, unsigned int unit, void *userdata, int opt, void *data, size_t *len) 27 | { 28 | return 0; 29 | } 30 | 31 | errno_t EPHandleConnect(kern_ctl_ref ctlref, struct sockaddr_ctl *sac, void **unitinfo) 32 | { 33 | return 0; 34 | } 35 | 36 | errno_t EPHandleDisconnect(kern_ctl_ref ctlref, unsigned int unit, void *unitinfo) 37 | { 38 | return 0; 39 | } 40 | 41 | errno_t ClevoControl::EPHandleWrite(kern_ctl_ref ctlref, unsigned int unit, void *userdata, mbuf_t m, int flags) 42 | { 43 | OSObject *params[3]; 44 | struct ECCtrl *ctrl; 45 | ctrl = (ECCtrl *)mbuf_data(m); 46 | params[0] = OSNumber::withNumber(ctrl->arg0, 8 * sizeof(uint32_t)); 47 | params[1] = OSNumber::withNumber(ctrl->arg1, 8 * sizeof(uint32_t)); 48 | params[2] = OSNumber::withNumber(ctrl->arg2, 8 * sizeof(uint32_t)); 49 | device->evaluateObject("WMIB", nullptr, params, 3); 50 | return 0; 51 | } 52 | 53 | bool ClevoControl::start(IOService *provider) 54 | { 55 | device = OSDynamicCast(IOACPIPlatformDevice, provider); 56 | if (device == nullptr || !IOService::start(provider)) 57 | return false; 58 | errno_t error; 59 | struct kern_ctl_reg ep_ctl; 60 | kern_ctl_ref kctlref; 61 | bzero(&ep_ctl, sizeof(ep_ctl)); 62 | ep_ctl.ctl_id = 0; 63 | ep_ctl.ctl_unit = 0; 64 | strlcpy(ep_ctl.ctl_name, "moe.datasone.clevocontrol.ctl", sizeof(ep_ctl.ctl_name)); 65 | ep_ctl.ctl_flags = CTL_FLAG_PRIVILEGED & CTL_FLAG_REG_ID_UNIT; 66 | ep_ctl.ctl_send = EPHandleWrite; 67 | ep_ctl.ctl_setopt = EPHandleSet; 68 | ep_ctl.ctl_getopt = EPHandleGet; 69 | ep_ctl.ctl_connect = EPHandleConnect; 70 | ep_ctl.ctl_disconnect = EPHandleDisconnect; 71 | error = ctl_register(&ep_ctl, &kctlref); 72 | return true; 73 | } 74 | 75 | void ClevoControl::stop(IOService *provider) 76 | { 77 | 78 | } 79 | -------------------------------------------------------------------------------- /ClevoControl/ClevoControl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ClevoControl_hpp 2 | #define ClevoControl_hpp 3 | 4 | #define ClevoControl moe_datasone_ClevoControl 5 | 6 | #include 7 | #include 8 | 9 | class ClevoControl : public IOService 10 | { 11 | OSDeclareDefaultStructors(moe_datasone_ClevoControl) 12 | 13 | public: 14 | virtual bool init(OSDictionary *dictionary = 0) override; 15 | virtual IOService *probe(IOService *provider, SInt32 *score) override; 16 | virtual bool start(IOService *provider) override; 17 | virtual void stop(IOService *provider) override; 18 | 19 | static errno_t EPHandleWrite(kern_ctl_ref ctlref, unsigned int unit, void *userdata, mbuf_t m, int flags); 20 | }; 21 | 22 | static IOACPIPlatformDevice *device; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /ClevoControl/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(MODULE_NAME) 15 | CFBundlePackageType 16 | KEXT 17 | CFBundleShortVersionString 18 | 0.0.1 19 | CFBundleVersion 20 | 0.0.1 21 | IOKitPersonalities 22 | 23 | Clevo Control 24 | 25 | CFBundleIdentifier 26 | ${MODULE_NAME} 27 | IOClass 28 | moe_datasone_ClevoControl 29 | IONameMatch 30 | 31 | MON00000 32 | MON0000 33 | 34 | IOProviderClass 35 | IOACPIPlatformDevice 36 | 37 | 38 | NSHumanReadableCopyright 39 | Copyright © 2017 datasone. All rights reserved. 40 | OSBundleLibraries 41 | 42 | com.apple.iokit.IOACPIFamily 43 | 1.0d1 44 | com.apple.kpi.iokit 45 | 9.0.0 46 | com.apple.kpi.libkern 47 | 9.0.0 48 | com.apple.kpi.bsd 49 | 9.0.0 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /ClevoKBFanControl/args.hxx: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2016 Taylor C. Richberger 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | /** \file args.hxx 23 | * \brief this single-header lets you use all of the args functionality 24 | * 25 | * The important stuff is done inside the args namespace 26 | */ 27 | 28 | #ifndef ARGS_HXX 29 | #define ARGS_HXX 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #ifdef ARGS_TESTNAMESPACE 43 | namespace argstest 44 | { 45 | #else 46 | 47 | /** \namespace args 48 | * \brief contains all the functionality of the args library 49 | */ 50 | namespace args 51 | { 52 | #endif 53 | /** Getter to grab the value from the argument type. 54 | * 55 | * If the Get() function of the type returns a reference, so does this, and 56 | * the value will be modifiable. 57 | */ 58 | template 59 | auto get(Option &option_) -> decltype(option_.Get()) 60 | { 61 | return option_.Get(); 62 | } 63 | 64 | /** (INTERNAL) Count UTF-8 glyphs 65 | * 66 | * This is not reliable, and will fail for combinatory glyphs, but it's 67 | * good enough here for now. 68 | * 69 | * \param string The string to count glyphs from 70 | * \return The UTF-8 glyphs in the string 71 | */ 72 | std::string::size_type Glyphs(const std::string &string_) 73 | { 74 | std::string::size_type length = 0; 75 | for (const char c: string_) 76 | { 77 | if ((c & 0xc0) != 0x80) 78 | { 79 | ++length; 80 | } 81 | } 82 | return length; 83 | } 84 | 85 | /** (INTERNAL) Wrap a string into a vector of lines 86 | * 87 | * This is quick and hacky, but works well enough. You can specify a 88 | * different width for the first line 89 | * 90 | * \param width The width of the body 91 | * \param the widtho f the first line, defaults to the width of the body 92 | * \return the vector of lines 93 | */ 94 | std::vector Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0) 95 | { 96 | // Preserve existing line breaks 97 | const auto newlineloc = in.find('\n'); 98 | if (newlineloc != in.npos) 99 | { 100 | auto first = Wrap(std::string(in, 0, newlineloc), width); 101 | auto second = Wrap(std::string(in, newlineloc + 1), width); 102 | first.insert( 103 | std::end(first), 104 | std::make_move_iterator(std::begin(second)), 105 | std::make_move_iterator(std::end(second))); 106 | return first; 107 | } 108 | if (firstlinewidth == 0) 109 | { 110 | firstlinewidth = width; 111 | } 112 | auto currentwidth = firstlinewidth; 113 | 114 | std::istringstream stream(in); 115 | std::vector output; 116 | std::ostringstream line; 117 | std::string::size_type linesize = 0; 118 | while (stream) 119 | { 120 | std::string item; 121 | stream >> item; 122 | auto itemsize = Glyphs(item); 123 | if ((linesize + 1 + itemsize) > currentwidth) 124 | { 125 | if (linesize > 0) 126 | { 127 | output.push_back(line.str()); 128 | line.str(std::string()); 129 | linesize = 0; 130 | currentwidth = width; 131 | } 132 | } 133 | if (itemsize > 0) 134 | { 135 | if (linesize) 136 | { 137 | ++linesize; 138 | line << " "; 139 | } 140 | line << item; 141 | linesize += itemsize; 142 | } 143 | } 144 | if (linesize > 0) 145 | { 146 | output.push_back(line.str()); 147 | } 148 | return output; 149 | } 150 | 151 | #ifdef ARGS_NOEXCEPT 152 | /// Error class, for when ARGS_NOEXCEPT is defined 153 | enum class Error 154 | { 155 | None, 156 | Usage, 157 | Parse, 158 | Validation, 159 | Map, 160 | Extra, 161 | Help 162 | }; 163 | #else 164 | /** Base error class 165 | */ 166 | class Error : public std::runtime_error 167 | { 168 | public: 169 | Error(const std::string &problem) : std::runtime_error(problem) {} 170 | virtual ~Error() {}; 171 | }; 172 | 173 | /** Errors that occur during usage 174 | */ 175 | class UsageError : public Error 176 | { 177 | public: 178 | UsageError(const std::string &problem) : Error(problem) {} 179 | virtual ~UsageError() {}; 180 | }; 181 | 182 | /** Errors that occur during regular parsing 183 | */ 184 | class ParseError : public Error 185 | { 186 | public: 187 | ParseError(const std::string &problem) : Error(problem) {} 188 | virtual ~ParseError() {}; 189 | }; 190 | 191 | /** Errors that are detected from group validation after parsing finishes 192 | */ 193 | class ValidationError : public Error 194 | { 195 | public: 196 | ValidationError(const std::string &problem) : Error(problem) {} 197 | virtual ~ValidationError() {}; 198 | }; 199 | 200 | /** Errors in map lookups 201 | */ 202 | class MapError : public ParseError 203 | { 204 | public: 205 | MapError(const std::string &problem) : ParseError(problem) {} 206 | virtual ~MapError() {}; 207 | }; 208 | 209 | /** Error that occurs when a singular flag is specified multiple times 210 | */ 211 | class ExtraError : public ParseError 212 | { 213 | public: 214 | ExtraError(const std::string &problem) : ParseError(problem) {} 215 | virtual ~ExtraError() {}; 216 | }; 217 | 218 | /** An exception that indicates that the user has requested help 219 | */ 220 | class Help : public Error 221 | { 222 | public: 223 | Help(const std::string &flag) : Error(flag) {} 224 | virtual ~Help() {}; 225 | }; 226 | #endif 227 | 228 | /** A simple unified option type for unified initializer lists for the Matcher class. 229 | */ 230 | struct EitherFlag 231 | { 232 | const bool isShort; 233 | const char shortFlag; 234 | const std::string longFlag; 235 | EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {} 236 | EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {} 237 | EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {} 238 | 239 | /** Get just the long flags from an initializer list of EitherFlags 240 | */ 241 | static std::unordered_set GetLong(std::initializer_list flags) 242 | { 243 | std::unordered_set longFlags; 244 | for (const EitherFlag &flag: flags) 245 | { 246 | if (!flag.isShort) 247 | { 248 | longFlags.insert(flag.longFlag); 249 | } 250 | } 251 | return longFlags; 252 | } 253 | 254 | /** Get just the short flags from an initializer list of EitherFlags 255 | */ 256 | static std::unordered_set GetShort(std::initializer_list flags) 257 | { 258 | std::unordered_set shortFlags; 259 | for (const EitherFlag &flag: flags) 260 | { 261 | if (flag.isShort) 262 | { 263 | shortFlags.insert(flag.shortFlag); 264 | } 265 | } 266 | return shortFlags; 267 | } 268 | }; 269 | 270 | 271 | 272 | /** A class of "matchers", specifying short and flags that can possibly be 273 | * matched. 274 | * 275 | * This is supposed to be constructed and then passed in, not used directly 276 | * from user code. 277 | */ 278 | class Matcher 279 | { 280 | private: 281 | const std::unordered_set shortFlags; 282 | const std::unordered_set longFlags; 283 | 284 | public: 285 | /** Specify short and long flags separately as iterators 286 | * 287 | * ex: `args::Matcher(shortFlags.begin(), shortFlags.end(), longFlags.begin(), longFlags.end())` 288 | */ 289 | template 290 | Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) : 291 | shortFlags(shortFlagsStart, shortFlagsEnd), 292 | longFlags(longFlagsStart, longFlagsEnd) 293 | {} 294 | 295 | /** Specify short and long flags separately as iterables 296 | * 297 | * ex: `args::Matcher(shortFlags, longFlags)` 298 | */ 299 | template 300 | Matcher(Short &&shortIn, Long &&longIn) : 301 | shortFlags(std::begin(shortIn), std::end(shortIn)), longFlags(std::begin(longIn), std::end(longIn)) 302 | {} 303 | 304 | /** Specify a mixed single initializer-list of both short and long flags 305 | * 306 | * This is the fancy one. It takes a single initializer list of 307 | * any number of any mixed kinds of flags. Chars are 308 | * automatically interpreted as short flags, and strings are 309 | * automatically interpreted as long flags: 310 | * 311 | * args::Matcher{'a'} 312 | * args::Matcher{"foo"} 313 | * args::Matcher{'h', "help"} 314 | * args::Matcher{"foo", 'f', 'F', "FoO"} 315 | */ 316 | Matcher(std::initializer_list in) : 317 | shortFlags(EitherFlag::GetShort(in)), longFlags(EitherFlag::GetLong(in)) {} 318 | 319 | Matcher(Matcher &&other) : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags)) 320 | {} 321 | 322 | ~Matcher() {} 323 | 324 | /** (INTERNAL) Check if there is a match of a short flag 325 | */ 326 | bool Match(const char flag) const 327 | { 328 | return shortFlags.find(flag) != shortFlags.end(); 329 | } 330 | 331 | /** (INTERNAL) Check if there is a match of a long flag 332 | */ 333 | bool Match(const std::string &flag) const 334 | { 335 | return longFlags.find(flag) != longFlags.end(); 336 | } 337 | 338 | /** (INTERNAL) Get all flag strings as a vector, with the prefixes embedded 339 | */ 340 | std::vector GetFlagStrings(const std::string &shortPrefix, const std::string &longPrefix) const 341 | { 342 | std::vector flagStrings; 343 | flagStrings.reserve(shortFlags.size() + longFlags.size()); 344 | for (const char flag: shortFlags) 345 | { 346 | flagStrings.emplace_back(shortPrefix + std::string(1, flag)); 347 | } 348 | for (const std::string &flag: longFlags) 349 | { 350 | flagStrings.emplace_back(longPrefix + flag); 351 | } 352 | return flagStrings; 353 | } 354 | 355 | /** (INTERNAL) Get all flag strings as a vector, with the prefixes and names embedded 356 | */ 357 | std::vector GetFlagStrings(const std::string &shortPrefix, const std::string &longPrefix, const std::string &name, const std::string &shortSeparator, const std::string longSeparator) const 358 | { 359 | const std::string bracedname(std::string("[") + name + "]"); 360 | std::vector flagStrings; 361 | flagStrings.reserve(shortFlags.size() + longFlags.size()); 362 | for (const char flag: shortFlags) 363 | { 364 | flagStrings.emplace_back(shortPrefix + std::string(1, flag) + shortSeparator + bracedname); 365 | } 366 | for (const std::string &flag: longFlags) 367 | { 368 | flagStrings.emplace_back(longPrefix + flag + longSeparator + bracedname); 369 | } 370 | return flagStrings; 371 | } 372 | }; 373 | 374 | /** Base class for all match types 375 | */ 376 | class Base 377 | { 378 | protected: 379 | bool matched; 380 | const std::string help; 381 | #ifdef ARGS_NOEXCEPT 382 | /// Only for ARGS_NOEXCEPT 383 | Error error; 384 | #endif 385 | 386 | public: 387 | Base(const std::string &help_) : matched(false), help(help_) {} 388 | virtual ~Base() {} 389 | 390 | virtual bool Matched() const noexcept 391 | { 392 | return matched; 393 | } 394 | 395 | operator bool() const noexcept 396 | { 397 | return Matched(); 398 | } 399 | 400 | virtual std::tuple GetDescription(const std::string &, const std::string &, const std::string &, const std::string &) const 401 | { 402 | std::tuple description; 403 | std::get<1>(description) = help; 404 | return description; 405 | } 406 | 407 | virtual void Reset() noexcept 408 | { 409 | matched = false; 410 | #ifdef ARGS_NOEXCEPT 411 | error = Error::None; 412 | #endif 413 | } 414 | 415 | #ifdef ARGS_NOEXCEPT 416 | /// Only for ARGS_NOEXCEPT 417 | virtual Error GetError() const 418 | { 419 | return error; 420 | } 421 | #endif 422 | }; 423 | 424 | /** Base class for all match types that have a name 425 | */ 426 | class NamedBase : public Base 427 | { 428 | protected: 429 | const std::string name; 430 | bool kickout; 431 | 432 | public: 433 | NamedBase(const std::string &name_, const std::string &help_) : Base(help_), name(name_), kickout(false) {} 434 | virtual ~NamedBase() {} 435 | 436 | virtual std::tuple GetDescription(const std::string &, const std::string &, const std::string &, const std::string &) const override 437 | { 438 | std::tuple description; 439 | std::get<0>(description) = Name(); 440 | std::get<1>(description) = help; 441 | return description; 442 | } 443 | virtual std::string Name() const 444 | { 445 | return name; 446 | } 447 | 448 | /// Sets a kick-out value for building subparsers 449 | void KickOut(bool kickout_) noexcept 450 | { 451 | this->kickout = kickout_; 452 | } 453 | 454 | /// Gets the kick-out value for building subparsers 455 | bool KickOut() const noexcept 456 | { 457 | return kickout; 458 | } 459 | }; 460 | 461 | /** Base class for all flag options 462 | */ 463 | class FlagBase : public NamedBase 464 | { 465 | private: 466 | const bool extraError; 467 | 468 | protected: 469 | const Matcher matcher; 470 | 471 | public: 472 | FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : NamedBase(name_, help_), extraError(extraError_), matcher(std::move(matcher_)) {} 473 | 474 | virtual ~FlagBase() {} 475 | 476 | virtual FlagBase *Match(const std::string &flag) 477 | { 478 | if (matcher.Match(flag)) 479 | { 480 | if (extraError && matched) 481 | { 482 | #ifdef ARGS_NOEXCEPT 483 | error = Error::Extra; 484 | #else 485 | std::ostringstream problem; 486 | problem << "Flag '" << flag << "' was passed multiple times, but is only allowed to be passed once"; 487 | throw ExtraError(problem.str()); 488 | #endif 489 | } 490 | matched = true; 491 | return this; 492 | } 493 | return nullptr; 494 | } 495 | 496 | virtual FlagBase *Match(const char flag) 497 | { 498 | if (matcher.Match(flag)) 499 | { 500 | if (extraError && matched) 501 | { 502 | #ifdef ARGS_NOEXCEPT 503 | error = Error::Extra; 504 | #else 505 | std::ostringstream problem; 506 | problem << "Flag '" << flag << "' was passed multiple times, but is only allowed to be passed once"; 507 | throw ExtraError(problem.str()); 508 | #endif 509 | } 510 | matched = true; 511 | return this; 512 | } 513 | return nullptr; 514 | } 515 | 516 | virtual std::tuple GetDescription(const std::string &shortPrefix, const std::string &longPrefix, const std::string &, const std::string &) const override 517 | { 518 | std::tuple description; 519 | const auto flagStrings = matcher.GetFlagStrings(shortPrefix, longPrefix); 520 | std::ostringstream flagstream; 521 | for (auto it = std::begin(flagStrings); it != std::end(flagStrings); ++it) 522 | { 523 | if (it != std::begin(flagStrings)) 524 | { 525 | flagstream << ", "; 526 | } 527 | flagstream << *it; 528 | } 529 | std::get<0>(description) = flagstream.str(); 530 | std::get<1>(description) = help; 531 | return description; 532 | } 533 | }; 534 | 535 | /** Base class for value-accepting flag options 536 | */ 537 | class ValueFlagBase : public FlagBase 538 | { 539 | public: 540 | ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {} 541 | virtual ~ValueFlagBase() {} 542 | virtual void ParseValue(const std::string &value) = 0; 543 | 544 | virtual std::tuple GetDescription(const std::string &shortPrefix, const std::string &longPrefix, const std::string &shortSeparator, const std::string &longSeparator) const override 545 | { 546 | std::tuple description; 547 | const auto flagStrings = matcher.GetFlagStrings(shortPrefix, longPrefix, Name(), shortSeparator, longSeparator); 548 | std::ostringstream flagstream; 549 | for (auto it = std::begin(flagStrings); it != std::end(flagStrings); ++it) 550 | { 551 | if (it != std::begin(flagStrings)) 552 | { 553 | flagstream << ", "; 554 | } 555 | flagstream << *it; 556 | } 557 | std::get<0>(description) = flagstream.str(); 558 | std::get<1>(description) = help; 559 | return description; 560 | } 561 | }; 562 | 563 | /** Base class for positional options 564 | */ 565 | class PositionalBase : public NamedBase 566 | { 567 | protected: 568 | bool ready; 569 | 570 | public: 571 | PositionalBase(const std::string &name_, const std::string &help_) : NamedBase(name_, help_), ready(true) {} 572 | virtual ~PositionalBase() {} 573 | 574 | bool Ready() 575 | { 576 | return ready; 577 | } 578 | 579 | virtual void ParseValue(const std::string &value_) = 0; 580 | 581 | virtual void Reset() noexcept override 582 | { 583 | matched = false; 584 | ready = true; 585 | #ifdef ARGS_NOEXCEPT 586 | error = Error::None; 587 | #endif 588 | } 589 | }; 590 | 591 | /** Class for all kinds of validating groups, including ArgumentParser 592 | */ 593 | class Group : public Base 594 | { 595 | private: 596 | std::vector children; 597 | std::function validator; 598 | 599 | public: 600 | /** Default validators 601 | */ 602 | struct Validators 603 | { 604 | static bool Xor(const Group &group) 605 | { 606 | return group.MatchedChildren() == 1; 607 | } 608 | 609 | static bool AtLeastOne(const Group &group) 610 | { 611 | return group.MatchedChildren() >= 1; 612 | } 613 | 614 | static bool AtMostOne(const Group &group) 615 | { 616 | return group.MatchedChildren() <= 1; 617 | } 618 | 619 | static bool All(const Group &group) 620 | { 621 | return group.Children().size() == group.MatchedChildren(); 622 | } 623 | 624 | static bool AllOrNone(const Group &group) 625 | { 626 | return (All(group) || None(group)); 627 | } 628 | 629 | static bool AllChildGroups(const Group &group) 630 | { 631 | return std::find_if(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool { 632 | return dynamic_cast(child) && !child->Matched(); 633 | }) == std::end(group.Children()); 634 | } 635 | 636 | static bool DontCare(const Group &) 637 | { 638 | return true; 639 | } 640 | 641 | static bool CareTooMuch(const Group &) 642 | { 643 | return false; 644 | } 645 | 646 | static bool None(const Group &group) 647 | { 648 | return group.MatchedChildren() == 0; 649 | } 650 | }; 651 | /// If help is empty, this group will not be printed in help output 652 | Group(const std::string &help_ = std::string(), const std::function &validator_ = Validators::DontCare) : Base(help_), validator(validator_) {} 653 | /// If help is empty, this group will not be printed in help output 654 | Group(Group &group_, const std::string &help_ = std::string(), const std::function &validator_ = Validators::DontCare) : Base(help_), validator(validator_) 655 | { 656 | group_.Add(*this); 657 | } 658 | virtual ~Group() {} 659 | 660 | /** Return the first FlagBase that matches flag, or nullptr 661 | * 662 | * \param flag The flag with prefixes stripped 663 | * \return the first matching FlagBase pointer, or nullptr if there is no match 664 | */ 665 | template 666 | FlagBase *Match(const T &flag) 667 | { 668 | for (Base *child: children) 669 | { 670 | if (FlagBase *flagBase = dynamic_cast(child)) 671 | { 672 | if (FlagBase *match = flagBase->Match(flag)) 673 | { 674 | return match; 675 | } 676 | } else if (Group *group = dynamic_cast(child)) 677 | { 678 | if (FlagBase *match = group->Match(flag)) 679 | { 680 | return match; 681 | } 682 | } 683 | } 684 | return nullptr; 685 | } 686 | 687 | /** Get the next ready positional, or nullptr if there is none 688 | * 689 | * \return the first ready PositionalBase pointer, or nullptr if there is no match 690 | */ 691 | PositionalBase *GetNextPositional() 692 | { 693 | for (Base *child: children) 694 | { 695 | auto next = dynamic_cast(child); 696 | auto group = dynamic_cast(child); 697 | if (group) 698 | { 699 | next = group->GetNextPositional(); 700 | } 701 | if (next && next->Ready()) 702 | { 703 | return next; 704 | } 705 | } 706 | return nullptr; 707 | } 708 | 709 | /** Get whether this has any FlagBase children 710 | * 711 | * \return Whether or not there are any FlagBase children 712 | */ 713 | bool HasFlag() const 714 | { 715 | for (Base *child: children) 716 | { 717 | if (dynamic_cast(child)) 718 | { 719 | return true; 720 | } 721 | if (auto group = dynamic_cast(child)) 722 | { 723 | if (group->HasFlag()) 724 | { 725 | return true; 726 | } 727 | } 728 | } 729 | return false; 730 | } 731 | 732 | /** Append a child to this Group. 733 | */ 734 | void Add(Base &child) 735 | { 736 | children.emplace_back(&child); 737 | } 738 | 739 | /** Get all this group's children 740 | */ 741 | const std::vector &Children() const 742 | { 743 | return children; 744 | } 745 | 746 | /** Count the number of matched children this group has 747 | */ 748 | std::vector::size_type MatchedChildren() const 749 | { 750 | return std::count_if(std::begin(children), std::end(children), [](const Base *child){return child->Matched();}); 751 | } 752 | 753 | /** Whether or not this group matches validation 754 | */ 755 | virtual bool Matched() const noexcept override 756 | { 757 | return validator(*this); 758 | } 759 | 760 | /** Get validation 761 | */ 762 | bool Get() const 763 | { 764 | return Matched(); 765 | } 766 | 767 | /** Get all the child descriptions for help generation 768 | */ 769 | std::vector> GetChildDescriptions(const std::string &shortPrefix, const std::string &longPrefix, const std::string &shortSeparator, const std::string &longSeparator, const unsigned int indent = 0) const 770 | { 771 | std::vector> descriptions; 772 | for (const auto &child: children) 773 | { 774 | if (const auto group = dynamic_cast(child)) 775 | { 776 | // Push that group description on the back if not empty 777 | unsigned char addindent = 0; 778 | if (!group->help.empty()) 779 | { 780 | descriptions.emplace_back(group->help, "", indent); 781 | addindent = 1; 782 | } 783 | auto groupDescriptions = group->GetChildDescriptions(shortPrefix, longPrefix, shortSeparator, longSeparator, indent + addindent); 784 | descriptions.insert( 785 | std::end(descriptions), 786 | std::make_move_iterator(std::begin(groupDescriptions)), 787 | std::make_move_iterator(std::end(groupDescriptions))); 788 | } else if (const auto named = dynamic_cast(child)) 789 | { 790 | const auto description = named->GetDescription(shortPrefix, longPrefix, shortSeparator, longSeparator); 791 | descriptions.emplace_back(std::get<0>(description), std::get<1>(description), indent); 792 | } 793 | } 794 | return descriptions; 795 | } 796 | 797 | /** Get the names of positional parameters 798 | */ 799 | std::vector GetPosNames() const 800 | { 801 | std::vector names; 802 | for (const auto &child: children) 803 | { 804 | if (const Group *group = dynamic_cast(child)) 805 | { 806 | auto groupNames = group->GetPosNames(); 807 | names.insert( 808 | std::end(names), 809 | std::make_move_iterator(std::begin(groupNames)), 810 | std::make_move_iterator(std::end(groupNames))); 811 | } else if (const PositionalBase *pos = dynamic_cast(child)) 812 | { 813 | names.emplace_back(pos->Name()); 814 | } 815 | } 816 | return names; 817 | } 818 | 819 | virtual void Reset() noexcept override 820 | { 821 | for (auto &child: children) 822 | { 823 | child->Reset(); 824 | } 825 | #ifdef ARGS_NOEXCEPT 826 | error = Error::None; 827 | #endif 828 | } 829 | 830 | #ifdef ARGS_NOEXCEPT 831 | /// Only for ARGS_NOEXCEPT 832 | virtual Error GetError() const override 833 | { 834 | if (error != Error::None) 835 | { 836 | return error; 837 | } 838 | 839 | auto it = std::find_if(std::begin(children), std::end(children), [](const Base *child){return child->GetError() != Error::None;}); 840 | if (it == std::end(children)) 841 | { 842 | return Error::None; 843 | } else 844 | { 845 | return (*it)->GetError(); 846 | } 847 | } 848 | #endif 849 | 850 | }; 851 | 852 | /** The main user facing command line argument parser class 853 | */ 854 | class ArgumentParser : public Group 855 | { 856 | private: 857 | std::string prog; 858 | std::string proglinePostfix; 859 | std::string description; 860 | std::string epilog; 861 | 862 | std::string longprefix; 863 | std::string shortprefix; 864 | 865 | std::string longseparator; 866 | 867 | std::string terminator; 868 | 869 | bool allowJoinedShortValue; 870 | bool allowJoinedLongValue; 871 | bool allowSeparateShortValue; 872 | bool allowSeparateLongValue; 873 | 874 | public: 875 | /** A simple structure of parameters for easy user-modifyable help menus 876 | */ 877 | struct HelpParams 878 | { 879 | /** The width of the help menu 880 | */ 881 | unsigned int width = 80; 882 | /** The indent of the program line 883 | */ 884 | unsigned int progindent = 2; 885 | /** The indent of the program trailing lines for long parameters 886 | */ 887 | unsigned int progtailindent = 4; 888 | /** The indent of the description and epilogs 889 | */ 890 | unsigned int descriptionindent = 4; 891 | /** The indent of the flags 892 | */ 893 | unsigned int flagindent = 6; 894 | /** The indent of the flag descriptions 895 | */ 896 | unsigned int helpindent = 40; 897 | /** The additional indent each group adds 898 | */ 899 | unsigned int eachgroupindent = 2; 900 | 901 | /** The minimum gutter between each flag and its help 902 | */ 903 | unsigned int gutter = 1; 904 | 905 | /** Show the terminator when both options and positional parameters are present 906 | */ 907 | bool showTerminator = true; 908 | 909 | /** Show the {OPTIONS} on the prog line when this is true 910 | */ 911 | bool showProglineOptions = true; 912 | 913 | /** Show the positionals on the prog line when this is true 914 | */ 915 | bool showProglinePositionals = true; 916 | } helpParams; 917 | ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string()) : 918 | Group("", Group::Validators::AllChildGroups), 919 | description(description_), 920 | epilog(epilog_), 921 | longprefix("--"), 922 | shortprefix("-"), 923 | longseparator("="), 924 | terminator("--"), 925 | allowJoinedShortValue(true), 926 | allowJoinedLongValue(true), 927 | allowSeparateShortValue(true), 928 | allowSeparateLongValue(true) {} 929 | 930 | /** The program name for help generation 931 | */ 932 | const std::string &Prog() const 933 | { return prog; } 934 | /** The program name for help generation 935 | */ 936 | void Prog(const std::string &prog_) 937 | { this->prog = prog_; } 938 | 939 | /** The description that appears on the prog line after options 940 | */ 941 | const std::string &ProglinePostfix() const 942 | { return proglinePostfix; } 943 | /** The description that appears on the prog line after options 944 | */ 945 | void ProglinePostfix(const std::string &proglinePostfix_) 946 | { this->proglinePostfix = proglinePostfix_; } 947 | 948 | /** The description that appears above options 949 | */ 950 | const std::string &Description() const 951 | { return description; } 952 | /** The description that appears above options 953 | */ 954 | void Description(const std::string &description_) 955 | { this->description = description_; } 956 | 957 | /** The description that appears below options 958 | */ 959 | const std::string &Epilog() const 960 | { return epilog; } 961 | /** The description that appears below options 962 | */ 963 | void Epilog(const std::string &epilog_) 964 | { this->epilog = epilog_; } 965 | 966 | /** The prefix for long flags 967 | */ 968 | const std::string &LongPrefix() const 969 | { return longprefix; } 970 | /** The prefix for long flags 971 | */ 972 | void LongPrefix(const std::string &longprefix_) 973 | { this->longprefix = longprefix_; } 974 | 975 | /** The prefix for short flags 976 | */ 977 | const std::string &ShortPrefix() const 978 | { return shortprefix; } 979 | /** The prefix for short flags 980 | */ 981 | void ShortPrefix(const std::string &shortprefix_) 982 | { this->shortprefix = shortprefix_; } 983 | 984 | /** The separator for long flags 985 | */ 986 | const std::string &LongSeparator() const 987 | { return longseparator; } 988 | /** The separator for long flags 989 | */ 990 | void LongSeparator(const std::string &longseparator_) 991 | { 992 | if (longseparator_.empty()) 993 | { 994 | #ifdef ARGS_NOEXCEPT 995 | error = Error::Usage; 996 | #else 997 | throw UsageError("longseparator can not be set to empty"); 998 | #endif 999 | } else 1000 | { 1001 | this->longseparator = longseparator_; 1002 | } 1003 | } 1004 | 1005 | /** The terminator that forcibly separates flags from positionals 1006 | */ 1007 | const std::string &Terminator() const 1008 | { return terminator; } 1009 | /** The terminator that forcibly separates flags from positionals 1010 | */ 1011 | void Terminator(const std::string &terminator_) 1012 | { this->terminator = terminator_; } 1013 | 1014 | /** Get the current argument separation parameters. 1015 | * 1016 | * See SetArgumentSeparations for details on what each one means. 1017 | */ 1018 | void GetArgumentSeparations( 1019 | bool &allowJoinedShortValue_, 1020 | bool &allowJoinedLongValue_, 1021 | bool &allowSeparateShortValue_, 1022 | bool &allowSeparateLongValue_) const 1023 | { 1024 | allowJoinedShortValue_ = this->allowJoinedShortValue; 1025 | allowJoinedLongValue_ = this->allowJoinedLongValue; 1026 | allowSeparateShortValue_ = this->allowSeparateShortValue; 1027 | allowSeparateLongValue_ = this->allowSeparateLongValue; 1028 | } 1029 | 1030 | /** Change allowed option separation. 1031 | * 1032 | * \param allowJoinedShortValue Allow a short flag that accepts an argument to be passed its argument immediately next to it (ie. in the same argv field) 1033 | * \param allowJoinedLongValue Allow a long flag that accepts an argument to be passed its argument separated by the longseparator (ie. in the same argv field) 1034 | * \param allowSeparateShortValue Allow a short flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) 1035 | * \param allowSeparateLongValue Allow a long flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) 1036 | */ 1037 | void SetArgumentSeparations( 1038 | const bool allowJoinedShortValue_, 1039 | const bool allowJoinedLongValue_, 1040 | const bool allowSeparateShortValue_, 1041 | const bool allowSeparateLongValue_) 1042 | { 1043 | this->allowJoinedShortValue = allowJoinedShortValue_; 1044 | this->allowJoinedLongValue = allowJoinedLongValue_; 1045 | this->allowSeparateShortValue = allowSeparateShortValue_; 1046 | this->allowSeparateLongValue = allowSeparateLongValue_; 1047 | } 1048 | 1049 | /** Pass the help menu into an ostream 1050 | */ 1051 | void Help(std::ostream &help_) const 1052 | { 1053 | bool hasoptions = false; 1054 | bool hasarguments = false; 1055 | 1056 | const auto description_text = Wrap(this->description, helpParams.width - helpParams.descriptionindent); 1057 | const auto epilog_text = Wrap(this->epilog, helpParams.width - helpParams.descriptionindent); 1058 | std::ostringstream prognameline; 1059 | prognameline << prog; 1060 | if (HasFlag()) 1061 | { 1062 | hasoptions = true; 1063 | if (helpParams.showProglineOptions) 1064 | { 1065 | prognameline << " {OPTIONS}"; 1066 | } 1067 | } 1068 | for (const std::string &posname: GetPosNames()) 1069 | { 1070 | hasarguments = true; 1071 | if (helpParams.showProglinePositionals) 1072 | { 1073 | prognameline << " [" << posname << ']'; 1074 | } 1075 | } 1076 | if (!proglinePostfix.empty()) 1077 | { 1078 | prognameline << ' ' << proglinePostfix; 1079 | } 1080 | const auto proglines = Wrap(prognameline.str(), helpParams.width - (helpParams.progindent + 4), helpParams.width - helpParams.progindent); 1081 | auto progit = std::begin(proglines); 1082 | if (progit != std::end(proglines)) 1083 | { 1084 | help_ << std::string(helpParams.progindent, ' ') << *progit << '\n'; 1085 | ++progit; 1086 | } 1087 | for (; progit != std::end(proglines); ++progit) 1088 | { 1089 | help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n'; 1090 | } 1091 | 1092 | help_ << '\n'; 1093 | 1094 | for (const auto &line: description_text) 1095 | { 1096 | help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; 1097 | } 1098 | help_ << "\n"; 1099 | help_ << std::string(helpParams.progindent, ' ') << "OPTIONS:\n\n"; 1100 | for (const auto &desc: GetChildDescriptions(shortprefix, longprefix, allowJoinedShortValue ? "" : " ", allowJoinedLongValue ? longseparator : " ")) 1101 | { 1102 | const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent; 1103 | const auto flags = Wrap(std::get<0>(desc), helpParams.width - (helpParams.flagindent + helpParams.helpindent + helpParams.gutter)); 1104 | const auto info = Wrap(std::get<1>(desc), helpParams.width - (helpParams.helpindent + groupindent)); 1105 | 1106 | std::string::size_type flagssize = 0; 1107 | for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit) 1108 | { 1109 | if (flagsit != std::begin(flags)) 1110 | { 1111 | help_ << '\n'; 1112 | } 1113 | help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit; 1114 | flagssize = Glyphs(*flagsit); 1115 | } 1116 | 1117 | auto infoit = std::begin(info); 1118 | // groupindent is on both sides of this inequality, and therefore can be removed 1119 | if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info)) 1120 | { 1121 | help_ << '\n'; 1122 | } else 1123 | { 1124 | // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here 1125 | help_ << std::string(helpParams.helpindent - (helpParams.flagindent + flagssize), ' ') << *infoit << '\n'; 1126 | ++infoit; 1127 | } 1128 | for (; infoit != std::end(info); ++infoit) 1129 | { 1130 | help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n'; 1131 | } 1132 | } 1133 | if (hasoptions && hasarguments && helpParams.showTerminator) 1134 | { 1135 | for (const auto &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", helpParams.width - helpParams.flagindent)) 1136 | { 1137 | help_ << std::string(helpParams.flagindent, ' ') << item << '\n'; 1138 | } 1139 | } 1140 | 1141 | help_ << "\n"; 1142 | for (const auto &line: epilog_text) 1143 | { 1144 | help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; 1145 | } 1146 | } 1147 | 1148 | /** Generate a help menu as a string. 1149 | * 1150 | * \return the help text as a single string 1151 | */ 1152 | std::string Help() const 1153 | { 1154 | std::ostringstream help_; 1155 | Help(help_); 1156 | return help_.str(); 1157 | } 1158 | 1159 | /** Parse all arguments. 1160 | * 1161 | * \param begin an iterator to the beginning of the argument list 1162 | * \param end an iterator to the past-the-end element of the argument list 1163 | * \return the iterator after the last parsed value. Only useful for kick-out 1164 | */ 1165 | template 1166 | It ParseArgs(It begin, It end) 1167 | { 1168 | // Reset all Matched statuses and errors 1169 | Reset(); 1170 | bool terminated = false; 1171 | 1172 | // Check all arg chunks 1173 | for (auto it = begin; it != end; ++it) 1174 | { 1175 | const auto &chunk = *it; 1176 | 1177 | if (!terminated && chunk == terminator) 1178 | { 1179 | terminated = true; 1180 | // If a long arg was found 1181 | } else if (!terminated && chunk.find(longprefix) == 0 && chunk.size() > longprefix.size()) 1182 | { 1183 | const auto argchunk = chunk.substr(longprefix.size()); 1184 | // Try to separate it, in case of a separator: 1185 | const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator); 1186 | // If the separator is in the argument, separate it. 1187 | const auto arg = (separator != argchunk.npos ? 1188 | std::string(argchunk, 0, separator) 1189 | : argchunk); 1190 | 1191 | if (auto base = Match(arg)) 1192 | { 1193 | if (auto argbase = dynamic_cast(base)) 1194 | { 1195 | if (separator != argchunk.npos) 1196 | { 1197 | if (allowJoinedLongValue) 1198 | { 1199 | argbase->ParseValue(argchunk.substr(separator + longseparator.size())); 1200 | } else 1201 | { 1202 | #ifdef ARGS_NOEXCEPT 1203 | error = Error::Parse; 1204 | return it; 1205 | #else 1206 | std::ostringstream problem; 1207 | problem << "Flag '" << arg << "' was passed a joined argument, but these are disallowed"; 1208 | throw ParseError(problem.str()); 1209 | #endif 1210 | } 1211 | } else 1212 | { 1213 | ++it; 1214 | if (it == end) 1215 | { 1216 | #ifdef ARGS_NOEXCEPT 1217 | error = Error::Parse; 1218 | return it; 1219 | #else 1220 | std::ostringstream problem; 1221 | problem << "Flag '" << arg << "' requires an argument but received none"; 1222 | throw ParseError(problem.str()); 1223 | #endif 1224 | } 1225 | 1226 | if (allowSeparateLongValue) 1227 | { 1228 | argbase->ParseValue(*it); 1229 | } else 1230 | { 1231 | #ifdef ARGS_NOEXCEPT 1232 | error = Error::Parse; 1233 | return it; 1234 | #else 1235 | std::ostringstream problem; 1236 | problem << "Flag '" << arg << "' was passed a separate argument, but these are disallowed"; 1237 | throw ParseError(problem.str()); 1238 | #endif 1239 | } 1240 | } 1241 | } else if (separator != argchunk.npos) 1242 | { 1243 | #ifdef ARGS_NOEXCEPT 1244 | error = Error::Parse; 1245 | return it; 1246 | #else 1247 | std::ostringstream problem; 1248 | problem << "Passed an argument into a non-argument flag: " << chunk; 1249 | throw ParseError(problem.str()); 1250 | #endif 1251 | } 1252 | 1253 | if (base->KickOut()) 1254 | { 1255 | return ++it; 1256 | } 1257 | } else 1258 | { 1259 | #ifdef ARGS_NOEXCEPT 1260 | error = Error::Parse; 1261 | return it; 1262 | #else 1263 | std::ostringstream problem; 1264 | problem << "Flag could not be matched: " << arg; 1265 | throw ParseError(problem.str()); 1266 | #endif 1267 | } 1268 | // Check short args 1269 | } else if (!terminated && chunk.find(shortprefix) == 0 && chunk.size() > shortprefix.size()) 1270 | { 1271 | const auto argchunk = chunk.substr(shortprefix.size()); 1272 | for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit) 1273 | { 1274 | const auto arg = *argit; 1275 | 1276 | if (auto base = Match(arg)) 1277 | { 1278 | if (auto argbase = dynamic_cast(base)) 1279 | { 1280 | const std::string value(++argit, std::end(argchunk)); 1281 | if (!value.empty()) 1282 | { 1283 | if (allowJoinedShortValue) 1284 | { 1285 | argbase->ParseValue(value); 1286 | } else 1287 | { 1288 | #ifdef ARGS_NOEXCEPT 1289 | error = Error::Parse; 1290 | return it; 1291 | #else 1292 | std::ostringstream problem; 1293 | problem << "Flag '" << arg << "' was passed a joined argument, but these are disallowed"; 1294 | throw ParseError(problem.str()); 1295 | #endif 1296 | } 1297 | } else 1298 | { 1299 | ++it; 1300 | if (it == end) 1301 | { 1302 | #ifdef ARGS_NOEXCEPT 1303 | error = Error::Parse; 1304 | return it; 1305 | #else 1306 | std::ostringstream problem; 1307 | problem << "Flag '" << arg << "' requires an argument but received none"; 1308 | throw ParseError(problem.str()); 1309 | #endif 1310 | } 1311 | 1312 | if (allowSeparateShortValue) 1313 | { 1314 | argbase->ParseValue(*it); 1315 | } else 1316 | { 1317 | #ifdef ARGS_NOEXCEPT 1318 | error = Error::Parse; 1319 | return it; 1320 | #else 1321 | std::ostringstream problem; 1322 | problem << "Flag '" << arg << "' was passed a separate argument, but these are disallowed"; 1323 | throw ParseError(problem.str()); 1324 | #endif 1325 | } 1326 | } 1327 | // Because this argchunk is done regardless 1328 | break; 1329 | } 1330 | 1331 | if (base->KickOut()) 1332 | { 1333 | return ++it; 1334 | } 1335 | } else 1336 | { 1337 | #ifdef ARGS_NOEXCEPT 1338 | error = Error::Parse; 1339 | return it; 1340 | #else 1341 | std::ostringstream problem; 1342 | problem << "Flag could not be matched: '" << arg << "'"; 1343 | throw ParseError(problem.str()); 1344 | #endif 1345 | } 1346 | } 1347 | } else 1348 | { 1349 | auto pos = GetNextPositional(); 1350 | if (pos) 1351 | { 1352 | pos->ParseValue(chunk); 1353 | 1354 | if (pos->KickOut()) 1355 | { 1356 | return ++it; 1357 | } 1358 | } else 1359 | { 1360 | #ifdef ARGS_NOEXCEPT 1361 | error = Error::Parse; 1362 | return it; 1363 | #else 1364 | std::ostringstream problem; 1365 | problem << "Passed in argument, but no positional arguments were ready to receive it: " << chunk; 1366 | throw ParseError(problem.str()); 1367 | #endif 1368 | } 1369 | } 1370 | } 1371 | if (!Matched()) 1372 | { 1373 | #ifdef ARGS_NOEXCEPT 1374 | error = Error::Validation; 1375 | #else 1376 | std::ostringstream problem; 1377 | problem << "Group validation failed somewhere!"; 1378 | throw ValidationError(problem.str()); 1379 | #endif 1380 | } 1381 | return end; 1382 | } 1383 | 1384 | /** Parse all arguments. 1385 | * 1386 | * \param args an iterable of the arguments 1387 | * \return the iterator after the last parsed value. Only useful for kick-out 1388 | */ 1389 | template 1390 | auto ParseArgs(const T &args) -> decltype(std::begin(args)) 1391 | { 1392 | return ParseArgs(std::begin(args), std::end(args)); 1393 | } 1394 | 1395 | /** Convenience function to parse the CLI from argc and argv 1396 | * 1397 | * Just assigns the program name and vectorizes arguments for passing into ParseArgs() 1398 | * 1399 | * \return whether or not all arguments were parsed. This works for detecting kick-out, but is generally useless as it can't do anything with it. 1400 | */ 1401 | bool ParseCLI(const int argc, const char * const * argv) 1402 | { 1403 | if (prog.empty()) 1404 | { 1405 | prog.assign(argv[0]); 1406 | } 1407 | const std::vector args(argv + 1, argv + argc); 1408 | return ParseArgs(args) == std::end(args); 1409 | } 1410 | }; 1411 | 1412 | std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser) 1413 | { 1414 | parser.Help(os); 1415 | return os; 1416 | } 1417 | 1418 | /** Boolean argument matcher 1419 | */ 1420 | class Flag : public FlagBase 1421 | { 1422 | public: 1423 | Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false): FlagBase(name_, help_, std::move(matcher_), extraError_) 1424 | { 1425 | group_.Add(*this); 1426 | } 1427 | 1428 | virtual ~Flag() {} 1429 | 1430 | /** Get whether this was matched 1431 | */ 1432 | bool Get() const 1433 | { 1434 | return Matched(); 1435 | } 1436 | }; 1437 | 1438 | /** Help flag class 1439 | * 1440 | * Works like a regular flag, but throws an instance of Help when it is matched 1441 | */ 1442 | class HelpFlag : public Flag 1443 | { 1444 | public: 1445 | HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_): Flag(group_, name_, help_, std::move(matcher_)) {} 1446 | 1447 | virtual ~HelpFlag() {} 1448 | 1449 | virtual FlagBase *Match(const std::string &arg) override 1450 | { 1451 | if (FlagBase::Match(arg)) 1452 | { 1453 | #ifdef ARGS_NOEXCEPT 1454 | error = Error::Help; 1455 | return this; 1456 | #else 1457 | throw Help(arg); 1458 | #endif 1459 | } 1460 | return nullptr; 1461 | } 1462 | 1463 | virtual FlagBase *Match(const char arg) override 1464 | { 1465 | if (FlagBase::Match(arg)) 1466 | { 1467 | #ifdef ARGS_NOEXCEPT 1468 | error = Error::Help; 1469 | return this; 1470 | #else 1471 | throw Help(std::string(1, arg)); 1472 | #endif 1473 | } 1474 | return nullptr; 1475 | } 1476 | 1477 | /** Get whether this was matched 1478 | */ 1479 | bool Get() const noexcept 1480 | { 1481 | return Matched(); 1482 | } 1483 | }; 1484 | 1485 | /** A flag class that simply counts the number of times it's matched 1486 | */ 1487 | class CounterFlag : public Flag 1488 | { 1489 | private: 1490 | const int startcount; 1491 | int count; 1492 | 1493 | public: 1494 | CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0): Flag(group_, name_, help_, std::move(matcher_)), startcount(startcount_), count(startcount_) {} 1495 | 1496 | virtual ~CounterFlag() {} 1497 | 1498 | virtual FlagBase *Match(const std::string &arg) override 1499 | { 1500 | auto me = FlagBase::Match(arg); 1501 | if (me) 1502 | { 1503 | ++count; 1504 | } 1505 | return me; 1506 | } 1507 | 1508 | virtual FlagBase *Match(const char arg) override 1509 | { 1510 | auto me = FlagBase::Match(arg); 1511 | if (me) 1512 | { 1513 | ++count; 1514 | } 1515 | return me; 1516 | } 1517 | 1518 | /** Get the count 1519 | */ 1520 | int &Get() noexcept 1521 | { 1522 | return count; 1523 | } 1524 | 1525 | virtual void Reset() noexcept override 1526 | { 1527 | FlagBase::Reset(); 1528 | count = startcount; 1529 | } 1530 | }; 1531 | 1532 | /** A default Reader class for argument classes 1533 | * 1534 | * Simply uses a std::istringstream to read into the destination type, and 1535 | * raises a ParseError if there are any characters left. 1536 | */ 1537 | template 1538 | struct ValueReader 1539 | { 1540 | bool operator ()(const std::string &name, const std::string &value, T &destination) 1541 | { 1542 | std::istringstream ss(value); 1543 | ss >> destination; 1544 | 1545 | if (ss.rdbuf()->in_avail() > 0) 1546 | { 1547 | #ifdef ARGS_NOEXCEPT 1548 | return false; 1549 | #else 1550 | std::ostringstream problem; 1551 | problem << "Argument '" << name << "' received invalid value type '" << value << "'"; 1552 | throw ParseError(problem.str()); 1553 | #endif 1554 | } 1555 | return true; 1556 | } 1557 | }; 1558 | 1559 | /** std::string specialization for ValueReader 1560 | * 1561 | * By default, stream extraction into a string splits on white spaces, and 1562 | * it is more efficient to ust copy a string into the destination. 1563 | */ 1564 | template <> 1565 | struct ValueReader 1566 | { 1567 | bool operator()(const std::string &, const std::string &value, std::string &destination) 1568 | { 1569 | destination.assign(value); 1570 | return true; 1571 | } 1572 | }; 1573 | 1574 | /** An argument-accepting flag class 1575 | * 1576 | * \tparam T the type to extract the argument as 1577 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 1578 | */ 1579 | template < 1580 | typename T, 1581 | typename Reader = ValueReader> 1582 | class ValueFlag : public ValueFlagBase 1583 | { 1584 | private: 1585 | T value; 1586 | Reader reader; 1587 | 1588 | public: 1589 | 1590 | ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), const bool extraError_ = false): ValueFlagBase(name_, help_, std::move(matcher_), extraError_), value(defaultValue_) 1591 | { 1592 | group_.Add(*this); 1593 | } 1594 | 1595 | virtual ~ValueFlag() {} 1596 | 1597 | virtual void ParseValue(const std::string &value_) override 1598 | { 1599 | #ifdef ARGS_NOEXCEPT 1600 | if (!reader(name, value_, this->value)) 1601 | { 1602 | error = Error::Parse; 1603 | } 1604 | #else 1605 | reader(name, value_, this->value); 1606 | #endif 1607 | } 1608 | 1609 | /** Get the value 1610 | */ 1611 | T &Get() noexcept 1612 | { 1613 | return value; 1614 | } 1615 | }; 1616 | 1617 | /** An argument-accepting flag class that pushes the found values into a list 1618 | * 1619 | * \tparam T the type to extract the argument as 1620 | * \tparam List the list type that houses the values 1621 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 1622 | */ 1623 | template < 1624 | typename T, 1625 | template class List = std::vector, 1626 | typename Reader = ValueReader> 1627 | class ValueFlagList : public ValueFlagBase 1628 | { 1629 | private: 1630 | using Container = List; 1631 | Container values; 1632 | Reader reader; 1633 | 1634 | public: 1635 | 1636 | typedef T value_type; 1637 | typedef typename Container::allocator_type allocator_type; 1638 | typedef typename Container::pointer pointer; 1639 | typedef typename Container::const_pointer const_pointer; 1640 | typedef T& reference; 1641 | typedef const T& const_reference; 1642 | typedef typename Container::size_type size_type; 1643 | typedef typename Container::difference_type difference_type; 1644 | typedef typename Container::iterator iterator; 1645 | typedef typename Container::const_iterator const_iterator; 1646 | typedef std::reverse_iterator reverse_iterator; 1647 | typedef std::reverse_iterator const_reverse_iterator; 1648 | 1649 | ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container()): ValueFlagBase(name_, help_, std::move(matcher_)), values(defaultValues_) 1650 | { 1651 | group_.Add(*this); 1652 | } 1653 | 1654 | virtual ~ValueFlagList() {} 1655 | 1656 | virtual void ParseValue(const std::string &value_) override 1657 | { 1658 | T v; 1659 | #ifdef ARGS_NOEXCEPT 1660 | if (!reader(name, value_, v)) 1661 | { 1662 | error = Error::Parse; 1663 | } 1664 | #else 1665 | reader(name, value_, v); 1666 | #endif 1667 | values.insert(std::end(values), v); 1668 | } 1669 | 1670 | /** Get the values 1671 | */ 1672 | Container &Get() noexcept 1673 | { 1674 | return values; 1675 | } 1676 | 1677 | virtual std::string Name() const override 1678 | { 1679 | return name + std::string("..."); 1680 | } 1681 | 1682 | virtual void Reset() noexcept override 1683 | { 1684 | ValueFlagBase::Reset(); 1685 | values.clear(); 1686 | } 1687 | 1688 | iterator begin() noexcept 1689 | { 1690 | return values.begin(); 1691 | } 1692 | 1693 | const_iterator begin() const noexcept 1694 | { 1695 | return values.begin(); 1696 | } 1697 | 1698 | const_iterator cbegin() const noexcept 1699 | { 1700 | return values.cbegin(); 1701 | } 1702 | 1703 | iterator end() noexcept 1704 | { 1705 | return values.end(); 1706 | } 1707 | 1708 | const_iterator end() const noexcept 1709 | { 1710 | return values.end(); 1711 | } 1712 | 1713 | const_iterator cend() const noexcept 1714 | { 1715 | return values.cend(); 1716 | } 1717 | }; 1718 | 1719 | /** A mapping value flag class 1720 | * 1721 | * \tparam K the type to extract the argument as 1722 | * \tparam T the type to store the result as 1723 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 1724 | * \tparam Map The Map type. Should operate like std::map or std::unordered_map 1725 | */ 1726 | template < 1727 | typename K, 1728 | typename T, 1729 | typename Reader = ValueReader, 1730 | template class Map = std::unordered_map> 1731 | class MapFlag : public ValueFlagBase 1732 | { 1733 | private: 1734 | const Map map; 1735 | T value; 1736 | Reader reader; 1737 | 1738 | public: 1739 | 1740 | MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map &map_, const T &defaultValue_ = T(), const bool extraError_ = false): ValueFlagBase(name_, help_, std::move(matcher_), extraError_), map(map_), value(defaultValue_) 1741 | { 1742 | group_.Add(*this); 1743 | } 1744 | 1745 | virtual ~MapFlag() {} 1746 | 1747 | virtual void ParseValue(const std::string &value_) override 1748 | { 1749 | K key; 1750 | #ifdef ARGS_NOEXCEPT 1751 | if (!reader(name, value_, key)) 1752 | { 1753 | error = Error::Parse; 1754 | } 1755 | #else 1756 | reader(name, value_, key); 1757 | #endif 1758 | auto it = map.find(key); 1759 | if (it == std::end(map)) 1760 | { 1761 | #ifdef ARGS_NOEXCEPT 1762 | error = Error::Map; 1763 | #else 1764 | std::ostringstream problem; 1765 | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; 1766 | throw MapError(problem.str()); 1767 | #endif 1768 | } else 1769 | { 1770 | this->value = it->second; 1771 | } 1772 | } 1773 | 1774 | /** Get the value 1775 | */ 1776 | T &Get() noexcept 1777 | { 1778 | return value; 1779 | } 1780 | }; 1781 | 1782 | /** A mapping value flag list class 1783 | * 1784 | * \tparam K the type to extract the argument as 1785 | * \tparam T the type to store the result as 1786 | * \tparam List the list type that houses the values 1787 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 1788 | * \tparam Map The Map type. Should operate like std::map or std::unordered_map 1789 | */ 1790 | template < 1791 | typename K, 1792 | typename T, 1793 | template class List = std::vector, 1794 | typename Reader = ValueReader, 1795 | template class Map = std::unordered_map> 1796 | class MapFlagList : public ValueFlagBase 1797 | { 1798 | private: 1799 | using Container = List; 1800 | const Map map; 1801 | Container values; 1802 | Reader reader; 1803 | 1804 | public: 1805 | typedef T value_type; 1806 | typedef typename Container::allocator_type allocator_type; 1807 | typedef typename Container::pointer pointer; 1808 | typedef typename Container::const_pointer const_pointer; 1809 | typedef T& reference; 1810 | typedef const T& const_reference; 1811 | typedef typename Container::size_type size_type; 1812 | typedef typename Container::difference_type difference_type; 1813 | typedef typename Container::iterator iterator; 1814 | typedef typename Container::const_iterator const_iterator; 1815 | typedef std::reverse_iterator reverse_iterator; 1816 | typedef std::reverse_iterator const_reverse_iterator; 1817 | 1818 | MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map &map_, const Container &defaultValues_ = Container()): ValueFlagBase(name_, help_, std::move(matcher_)), map(map_), values(defaultValues_) 1819 | { 1820 | group_.Add(*this); 1821 | } 1822 | 1823 | virtual ~MapFlagList() {} 1824 | 1825 | virtual void ParseValue(const std::string &value) override 1826 | { 1827 | K key; 1828 | #ifdef ARGS_NOEXCEPT 1829 | if (!reader(name, value, key)) 1830 | { 1831 | error = Error::Parse; 1832 | } 1833 | #else 1834 | reader(name, value, key); 1835 | #endif 1836 | auto it = map.find(key); 1837 | if (it == std::end(map)) 1838 | { 1839 | #ifdef ARGS_NOEXCEPT 1840 | error = Error::Map; 1841 | #else 1842 | std::ostringstream problem; 1843 | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; 1844 | throw MapError(problem.str()); 1845 | #endif 1846 | } else 1847 | { 1848 | this->values.emplace_back(it->second); 1849 | } 1850 | } 1851 | 1852 | /** Get the value 1853 | */ 1854 | Container &Get() noexcept 1855 | { 1856 | return values; 1857 | } 1858 | 1859 | virtual std::string Name() const override 1860 | { 1861 | return name + std::string("..."); 1862 | } 1863 | 1864 | virtual void Reset() noexcept override 1865 | { 1866 | ValueFlagBase::Reset(); 1867 | values.clear(); 1868 | } 1869 | 1870 | iterator begin() noexcept 1871 | { 1872 | return values.begin(); 1873 | } 1874 | 1875 | const_iterator begin() const noexcept 1876 | { 1877 | return values.begin(); 1878 | } 1879 | 1880 | const_iterator cbegin() const noexcept 1881 | { 1882 | return values.cbegin(); 1883 | } 1884 | 1885 | iterator end() noexcept 1886 | { 1887 | return values.end(); 1888 | } 1889 | 1890 | const_iterator end() const noexcept 1891 | { 1892 | return values.end(); 1893 | } 1894 | 1895 | const_iterator cend() const noexcept 1896 | { 1897 | return values.cend(); 1898 | } 1899 | }; 1900 | 1901 | /** A positional argument class 1902 | * 1903 | * \tparam T the type to extract the argument as 1904 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 1905 | */ 1906 | template < 1907 | typename T, 1908 | typename Reader = ValueReader> 1909 | class Positional : public PositionalBase 1910 | { 1911 | private: 1912 | T value; 1913 | Reader reader; 1914 | public: 1915 | Positional(Group &group_, const std::string &name_, const std::string &help_, const T &defaultValue_ = T()): PositionalBase(name_, help_), value(defaultValue_) 1916 | { 1917 | group_.Add(*this); 1918 | } 1919 | 1920 | virtual ~Positional() {} 1921 | 1922 | virtual void ParseValue(const std::string &value_) override 1923 | { 1924 | #ifdef ARGS_NOEXCEPT 1925 | if (!reader(name, value_, this->value)) 1926 | { 1927 | error = Error::Parse; 1928 | } 1929 | #else 1930 | reader(name, value_, this->value); 1931 | #endif 1932 | ready = false; 1933 | matched = true; 1934 | } 1935 | 1936 | /** Get the value 1937 | */ 1938 | T &Get() noexcept 1939 | { 1940 | return value; 1941 | } 1942 | }; 1943 | 1944 | /** A positional argument class that pushes the found values into a list 1945 | * 1946 | * \tparam T the type to extract the argument as 1947 | * \tparam List the list type that houses the values 1948 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 1949 | */ 1950 | template < 1951 | typename T, 1952 | template class List = std::vector, 1953 | typename Reader = ValueReader> 1954 | class PositionalList : public PositionalBase 1955 | { 1956 | private: 1957 | using Container = List; 1958 | Container values; 1959 | Reader reader; 1960 | 1961 | public: 1962 | typedef T value_type; 1963 | typedef typename Container::allocator_type allocator_type; 1964 | typedef typename Container::pointer pointer; 1965 | typedef typename Container::const_pointer const_pointer; 1966 | typedef T& reference; 1967 | typedef const T& const_reference; 1968 | typedef typename Container::size_type size_type; 1969 | typedef typename Container::difference_type difference_type; 1970 | typedef typename Container::iterator iterator; 1971 | typedef typename Container::const_iterator const_iterator; 1972 | typedef std::reverse_iterator reverse_iterator; 1973 | typedef std::reverse_iterator const_reverse_iterator; 1974 | 1975 | PositionalList(Group &group_, const std::string &name_, const std::string &help_, const Container &defaultValues_ = Container()): PositionalBase(name_, help_), values(defaultValues_) 1976 | { 1977 | group_.Add(*this); 1978 | } 1979 | 1980 | virtual ~PositionalList() {} 1981 | 1982 | virtual void ParseValue(const std::string &value_) override 1983 | { 1984 | T v; 1985 | #ifdef ARGS_NOEXCEPT 1986 | if (!reader(name, value_, v)) 1987 | { 1988 | error = Error::Parse; 1989 | } 1990 | #else 1991 | reader(name, value_, v); 1992 | #endif 1993 | values.insert(std::end(values), v); 1994 | matched = true; 1995 | } 1996 | 1997 | virtual std::string Name() const override 1998 | { 1999 | return name + std::string("..."); 2000 | } 2001 | 2002 | /** Get the values 2003 | */ 2004 | Container &Get() noexcept 2005 | { 2006 | return values; 2007 | } 2008 | 2009 | virtual void Reset() noexcept override 2010 | { 2011 | PositionalBase::Reset(); 2012 | values.clear(); 2013 | } 2014 | 2015 | iterator begin() noexcept 2016 | { 2017 | return values.begin(); 2018 | } 2019 | 2020 | const_iterator begin() const noexcept 2021 | { 2022 | return values.begin(); 2023 | } 2024 | 2025 | const_iterator cbegin() const noexcept 2026 | { 2027 | return values.cbegin(); 2028 | } 2029 | 2030 | iterator end() noexcept 2031 | { 2032 | return values.end(); 2033 | } 2034 | 2035 | const_iterator end() const noexcept 2036 | { 2037 | return values.end(); 2038 | } 2039 | 2040 | const_iterator cend() const noexcept 2041 | { 2042 | return values.cend(); 2043 | } 2044 | }; 2045 | 2046 | /** A positional argument mapping class 2047 | * 2048 | * \tparam K the type to extract the argument as 2049 | * \tparam T the type to store the result as 2050 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 2051 | * \tparam Map The Map type. Should operate like std::map or std::unordered_map 2052 | */ 2053 | template < 2054 | typename K, 2055 | typename T, 2056 | typename Reader = ValueReader, 2057 | template class Map = std::unordered_map> 2058 | class MapPositional : public PositionalBase 2059 | { 2060 | private: 2061 | const Map map; 2062 | T value; 2063 | Reader reader; 2064 | 2065 | public: 2066 | 2067 | MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map &map_, const T &defaultValue_ = T()): PositionalBase(name_, help_), map(map_), value(defaultValue_) 2068 | { 2069 | group_.Add(*this); 2070 | } 2071 | 2072 | virtual ~MapPositional() {} 2073 | 2074 | virtual void ParseValue(const std::string &value_) override 2075 | { 2076 | K key; 2077 | #ifdef ARGS_NOEXCEPT 2078 | if (!reader(name, value_, key)) 2079 | { 2080 | error = Error::Parse; 2081 | } 2082 | #else 2083 | reader(name, value_, key); 2084 | #endif 2085 | auto it = map.find(key); 2086 | if (it == std::end(map)) 2087 | { 2088 | #ifdef ARGS_NOEXCEPT 2089 | error = Error::Map; 2090 | #else 2091 | std::ostringstream problem; 2092 | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; 2093 | throw MapError(problem.str()); 2094 | #endif 2095 | } else 2096 | { 2097 | this->value = it->second; 2098 | ready = false; 2099 | matched = true; 2100 | } 2101 | } 2102 | 2103 | /** Get the value 2104 | */ 2105 | T &Get() noexcept 2106 | { 2107 | return value; 2108 | } 2109 | }; 2110 | 2111 | /** A positional argument mapping list class 2112 | * 2113 | * \tparam K the type to extract the argument as 2114 | * \tparam T the type to store the result as 2115 | * \tparam List the list type that houses the values 2116 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 2117 | * \tparam Map The Map type. Should operate like std::map or std::unordered_map 2118 | */ 2119 | template < 2120 | typename K, 2121 | typename T, 2122 | template class List = std::vector, 2123 | typename Reader = ValueReader, 2124 | template class Map = std::unordered_map> 2125 | class MapPositionalList : public PositionalBase 2126 | { 2127 | private: 2128 | using Container = List; 2129 | 2130 | const Map map; 2131 | Container values; 2132 | Reader reader; 2133 | 2134 | public: 2135 | typedef T value_type; 2136 | typedef typename Container::allocator_type allocator_type; 2137 | typedef typename Container::pointer pointer; 2138 | typedef typename Container::const_pointer const_pointer; 2139 | typedef T& reference; 2140 | typedef const T& const_reference; 2141 | typedef typename Container::size_type size_type; 2142 | typedef typename Container::difference_type difference_type; 2143 | typedef typename Container::iterator iterator; 2144 | typedef typename Container::const_iterator const_iterator; 2145 | typedef std::reverse_iterator reverse_iterator; 2146 | typedef std::reverse_iterator const_reverse_iterator; 2147 | 2148 | MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map &map_, const Container &defaultValues_ = Container()): PositionalBase(name_, help_), map(map_), values(defaultValues_) 2149 | { 2150 | group_.Add(*this); 2151 | } 2152 | 2153 | virtual ~MapPositionalList() {} 2154 | 2155 | virtual void ParseValue(const std::string &value_) override 2156 | { 2157 | K key; 2158 | #ifdef ARGS_NOEXCEPT 2159 | if (!reader(name, value_, key)) 2160 | { 2161 | error = Error::Parse; 2162 | } 2163 | #else 2164 | reader(name, value_, key); 2165 | #endif 2166 | auto it = map.find(key); 2167 | if (it == std::end(map)) 2168 | { 2169 | #ifdef ARGS_NOEXCEPT 2170 | error = Error::Map; 2171 | #else 2172 | std::ostringstream problem; 2173 | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; 2174 | throw MapError(problem.str()); 2175 | #endif 2176 | } else 2177 | { 2178 | this->values.emplace_back(it->second); 2179 | matched = true; 2180 | } 2181 | } 2182 | 2183 | /** Get the value 2184 | */ 2185 | Container &Get() noexcept 2186 | { 2187 | return values; 2188 | } 2189 | 2190 | virtual std::string Name() const override 2191 | { 2192 | return name + std::string("..."); 2193 | } 2194 | 2195 | virtual void Reset() noexcept override 2196 | { 2197 | PositionalBase::Reset(); 2198 | values.clear(); 2199 | } 2200 | 2201 | iterator begin() noexcept 2202 | { 2203 | return values.begin(); 2204 | } 2205 | 2206 | const_iterator begin() const noexcept 2207 | { 2208 | return values.begin(); 2209 | } 2210 | 2211 | const_iterator cbegin() const noexcept 2212 | { 2213 | return values.cbegin(); 2214 | } 2215 | 2216 | iterator end() noexcept 2217 | { 2218 | return values.end(); 2219 | } 2220 | 2221 | const_iterator end() const noexcept 2222 | { 2223 | return values.end(); 2224 | } 2225 | 2226 | const_iterator cend() const noexcept 2227 | { 2228 | return values.cend(); 2229 | } 2230 | }; 2231 | } 2232 | 2233 | #endif 2234 | -------------------------------------------------------------------------------- /ClevoKBFanControl/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // ClevoKBFanControl 4 | // 5 | // Created by datasone on 8/8/2017. 6 | // Copyright © 2017 datasone. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "args.hxx" 15 | #include "ECCtrl.h" 16 | 17 | #define SET_KB_LED 0x67 18 | #define SET_FAN 0x79 19 | 20 | using std::string; 21 | 22 | void sendctl(struct ECCtrl ctrl) 23 | { 24 | struct ctl_info info; 25 | struct sockaddr_ctl addr; 26 | int fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); 27 | if (fd != -1) 28 | { 29 | bzero(&addr, sizeof(addr)); 30 | addr.sc_len = sizeof(addr); 31 | addr.sc_family = AF_SYSTEM; 32 | addr.ss_sysaddr = AF_SYS_CONTROL; 33 | memset(&info, 0, sizeof(info)); 34 | strcpy(info.ctl_name, "moe.datasone.clevocontrol.ctl"); 35 | if (ioctl(fd, CTLIOCGINFO, &info)) 36 | { 37 | exit(-1); 38 | } 39 | addr.sc_id = info.ctl_id; 40 | addr.sc_unit = 0; 41 | if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_ctl))) 42 | exit(-1); 43 | send(fd, &ctrl, sizeof(ECCtrl), 0); 44 | } 45 | } 46 | 47 | int main(int argc, const char * argv[]) { 48 | args::ArgumentParser parser("Control keyboard backlight & fan for Clevo laptop. Only for full color keyboard backlight(without extra)", "Note that:\n You need to toggle backlight on manually.\n Please set three fan parameters simultaneously(I was just too lazy to implement read operations)\n The color options will be ignored if toggle option is set and colors option will be ignored when color option is set."); 49 | args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); 50 | args::ValueFlag bltoggle(parser, "ON|OFF", "Toggle keyboard backlight", {'l', "light"}); 51 | args::ValueFlag mode(parser, "BREATHE|CYCLE|DANCE|FLASH|RANDOM_COLOR|TEMPO|WAVE", "Set keyboard backlight mode", {'m', "mode"}); 52 | args::ValueFlag brightness(parser, "BRIGHTNESS(0|1|2|3)", "Set keyboard backlight brightness", {'b', "brightness"}); 53 | args::ValueFlag color(parser, "COLOR", "Keyboard backlight color in RGB hex", {'c', "color"}); 54 | args::ValueFlag colorList(parser, "COLORLIST", "Keyboard backlight colors, from left to right, split by comma", {"cl", "colors"}); 55 | args::Flag autoFan(parser, "auto", "Set fan speed to auto", {'a', "auto"}); 56 | args::ValueFlag startTemp(parser, "TEMPERATURE", "Fan start temperature", {"stt", "startTemp"}); 57 | args::ValueFlag stopTemp(parser, "TEMPERATURE", "Fan stop temperature", {"spt", "stopTemp"}); 58 | args::ValueFlag fanMaxSpeed(parser, "PERCENTAGE", "Max fan speed", {'s', "speed"}); 59 | try 60 | { 61 | parser.ParseCLI(argc, argv); 62 | } 63 | catch (args::Help) 64 | { 65 | std::cout << parser; 66 | return 0; 67 | } 68 | catch (args::ValidationError e) 69 | { 70 | std::cerr << e.what() << std::endl; 71 | std::cerr << parser; 72 | return 1; 73 | } 74 | struct ECCtrl ctrl; 75 | if (bltoggle) 76 | { 77 | string state = args::get(bltoggle); 78 | ctrl.arg0 = 0; 79 | ctrl.arg1 = SET_KB_LED; 80 | ctrl.arg2 = 0xE0000000; 81 | if (state == "OFF" || state == "off") 82 | ctrl.arg2 |= 0x003001; 83 | else if (state == "ON" || state == "on") 84 | ctrl.arg2 |= 0x07F001; 85 | else 86 | { 87 | std::cerr << "Wrong state!"; 88 | return 1; 89 | } 90 | sendctl(ctrl); 91 | } 92 | else if (mode) 93 | { 94 | string state = args::get(mode); 95 | struct ECCtrl ctrl2; 96 | ctrl2.arg0 = 0; 97 | ctrl2.arg1 = SET_KB_LED; 98 | ctrl2.arg2 = 0x10000000; 99 | ctrl.arg0 = 0; 100 | ctrl.arg1 = SET_KB_LED; 101 | if (state == "BREATHE" || state == "breathe") 102 | ctrl.arg2 = 0x1002A000; 103 | else if (state == "CYCLE" || state == "cycle") 104 | ctrl.arg2 = 0x33010000; 105 | else if (state == "DANCE" || state == "dance") 106 | ctrl.arg2 = 0x80000000; 107 | else if (state == "FLASH" || state == "flash") 108 | ctrl.arg2 = 0xA0000000; 109 | else if (state == "RANDOM_COLOR" || state == "random_color") 110 | ctrl.arg2 = 0x70000000; 111 | else if (state == "TEMPO" || state == "tempo") 112 | ctrl.arg2 = 0x90000000; 113 | else if (state == "WAVE" || state == "wave") 114 | ctrl.arg2 = 0xB0000000; 115 | else 116 | { 117 | std::cerr << "Wrong mode!"; 118 | return 1; 119 | } 120 | sendctl(ctrl2); 121 | sendctl(ctrl); 122 | } 123 | else if (color) 124 | { 125 | string state = args::get(color); 126 | if (state.size() != 6) 127 | { 128 | std::cerr << "Wrong color!"; 129 | return 1; 130 | } 131 | string state_brg = state.substr(4, 2) + state.substr(0, 2) + state.substr(2, 2); 132 | uint32_t i = std::stoul(state_brg, nullptr, 16); 133 | struct ECCtrl ctrl2; 134 | ctrl2.arg0 = 0; 135 | ctrl2.arg1 = SET_KB_LED; 136 | ctrl2.arg2 = 0x10000000; 137 | sendctl(ctrl2); 138 | ctrl.arg0 = 0; 139 | ctrl.arg1 = SET_KB_LED; 140 | ctrl.arg2 = 0xF0000000; 141 | ctrl.arg2 |= i; 142 | sendctl(ctrl); 143 | ctrl.arg2 = 0xF1000000; 144 | ctrl.arg2 |= i; 145 | sendctl(ctrl); 146 | ctrl.arg2 = 0xF2000000; 147 | ctrl.arg2 |= i; 148 | sendctl(ctrl); 149 | } 150 | else if (colorList) 151 | { 152 | string stateList = args::get(colorList); 153 | if (stateList.size() != 20) 154 | { 155 | std::cerr << "Wrong color!"; 156 | return 1; 157 | } 158 | struct ECCtrl ctrl2; 159 | ctrl2.arg0 = 0; 160 | ctrl2.arg1 = SET_KB_LED; 161 | ctrl2.arg2 = 0x10000000; 162 | sendctl(ctrl2); 163 | ctrl.arg0 = 0; 164 | ctrl.arg1 = SET_KB_LED; 165 | string colors[3]; 166 | for (int i = 0; i < 3; ++i) { 167 | colors[i] = stateList.substr(6 * i + i, 6); 168 | colors[i] = colors[i].substr(4, 2) + colors[i].substr(0, 2) + colors[i].substr(2, 2); 169 | uint32_t j = std::stoul(colors[i], nullptr, 16); 170 | ctrl.arg2 = 0xF0000000 + i * 0x1000000; 171 | ctrl.arg2 |= j; 172 | sendctl(ctrl); 173 | } 174 | } 175 | if (brightness) { 176 | uint8_t lvl_to_raw[] = { 63, 126, 189, 252 }; 177 | int b = args::get(brightness); 178 | if (b < 0 || b > 3) 179 | { 180 | std::cerr << "Wrong brightness value!"; 181 | return 1; 182 | } 183 | ctrl.arg0 = 0; 184 | ctrl.arg1 = SET_KB_LED; 185 | ctrl.arg2 = 0xF4000000 | lvl_to_raw[b]; 186 | sendctl(ctrl); 187 | } 188 | if (autoFan) 189 | { 190 | ctrl.arg0 = 0; 191 | ctrl.arg1 = SET_FAN; 192 | ctrl.arg2 = 0x1000000; 193 | sendctl(ctrl); 194 | ctrl.arg2 = 0; 195 | ctrl.arg2 |= 0x7000000; 196 | sendctl(ctrl); 197 | } 198 | else if (startTemp && stopTemp && fanMaxSpeed) 199 | { 200 | int ctrlData = (args::get(fanMaxSpeed) << 16) + (args::get(stopTemp) << 8) + args::get(startTemp); 201 | ctrlData |= 0x7000000; 202 | ctrl.arg0 = 0; 203 | ctrl.arg1 = SET_FAN; 204 | ctrl.arg2 = ctrlData; 205 | sendctl(ctrl); 206 | } 207 | return 0; 208 | } 209 | -------------------------------------------------------------------------------- /ECCtrl.h: -------------------------------------------------------------------------------- 1 | // 2 | // ECCtrl.h 3 | // ClevoControl 4 | // 5 | // Created by datasone on 9/8/2017. 6 | // Copyright © 2017 datasone. All rights reserved. 7 | // 8 | 9 | #ifndef ECCtrl_h 10 | #define ECCtrl_h 11 | 12 | struct ECCtrl { 13 | uint32_t arg0, arg1, arg2; 14 | }; 15 | 16 | #endif /* ECCtrl_h */ 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 datasone 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ClevoControl 2 | Control keyboard backlight and fan policy for Clevo Hackintosh 3 | This project only supports clevo laptops with full-color backlight support. However, you can easily modify it to fit in 8-color backlight keyboards. 4 | I have only tested it on my Clevo P650RS so I won't promise it will work, but it should. 5 | 6 | __DEPRECATED__: As Apple removed Nvidia support in recent macOS versions, I stopped using Clevo laptop as Hackintosh and thus not going to add new features to this kext. You may refer to [ClevoService](https://github.com/FreeJHack/ClevoService), which has more features such as keyboard controls and surviving through power off using NVRAM. 7 | 8 | # How to use 9 | Directly calling WMBB method in WMI device will lead to a kernel panic. I have no idea about why it's happening, so you should add following code into your DSDT: 10 | 11 | ``` 12 | Device (SMCD) 13 | { 14 | Name (_HID, EisaId ("PNP0C02")) 15 | Name (_CID, "MON00000") 16 | Method (WMIB, 3, Serialized) 17 | { 18 | \_SB.WMI.WMBB(Arg0, Arg1, Arg2) 19 | } 20 | } 21 | ``` 22 | 23 | 24 | You may already have SMCD device in your DSDT, then please ensure one of your \_HID or \_CID is "MON0000" or "MON00000" and add WMIB method into it. 25 | 26 | After you edit your DSDT, install ClevoControl.kext to /L/E (injecting it using Clover cause kernel panic) and use ClevoKBFanControl (a small command-line program) to control settings. 27 | 28 | Show usage by `ClevoKBFanControl -h`. 29 | 30 | This project uses [args](https://github.com/Taywee/args) for parsing command-line arguments. And the WMI operate codes used in this project comes from [clover-xsm-wmi](https://github.com/sonnym/clevo-xsm-wmi) and reverse engineering Clevo Control Center by myself. 31 | 32 | # Some Extra information 33 | ## Fan Speed 34 | To actually show the fan speed in HWSensors.app, you must have 35 | `FakeSMC_ACPISensors.kext` installed (both the kext and the app are available in 36 | [Rehabman's FakeSMC](https://bitbucket.org/RehabMan/os-x-fakesmc-kozlek/downloads/)), 37 | and edit the DSDT. For my laptop with 3 fans, add these to SMCD device (it uses B1B2 38 | method, add it if you don't have it in your DSDT): 39 | 40 | ``` 41 | Name (TACH, Package (0x06) 42 | { 43 | "CPU Fan", "FAN0", 44 | "GPU Fan #1", "FAN1", 45 | "GPU Fan #2", "FAN2" 46 | }) // Define fan names 47 | 48 | Method (FAN0, 0, Serialized) 49 | { 50 | If (\_SB.PCI0.LPCB.EC.ECOK) 51 | { 52 | Local0 = B1B2(\_SB.PCI0.LPCB.EC.FC01, \_SB.PCI0.LPCB.EC.FC00) 53 | If (Local0 <= 0) 54 | { 55 | Return (0) 56 | } 57 | Local0 = 2156220 / Local0 58 | Return (Local0) 59 | } 60 | Return (0) 61 | } 62 | 63 | Method (FAN1, 0, Serialized) 64 | { 65 | If (\_SB.PCI0.LPCB.EC.ECOK) 66 | { 67 | Local0 = B1B2(\_SB.PCI0.LPCB.EC.FG01, \_SB.PCI0.LPCB.EC.FG00) 68 | If (Local0 <= 0) 69 | { 70 | Return (0) 71 | } 72 | Local0 = 2156220 / Local0 73 | Return (Local0) 74 | } 75 | Return (0) 76 | } 77 | 78 | Method (FAN2, 0, Serialized) 79 | { 80 | If (\_SB.PCI0.LPCB.EC.ECOK) 81 | { 82 | Local0 = B1B2(\_SB.PCI0.LPCB.EC.FG11, \_SB.PCI0.LPCB.EC.FG10) 83 | If (Local0 <= 0) 84 | { 85 | Return (0) 86 | } 87 | Local0 = 2156220 / Local0 88 | Return (Local0) 89 | } 90 | Return (0) 91 | } 92 | ``` 93 | 94 | The B1B2 method can be added with MaciASL: 95 | ``` 96 | into method label B1B2 remove_entry; 97 | into definitionblock code_regex . insert 98 | begin 99 | Method (B1B2, 2, NotSerialized) { Return(Or(Arg0, ShiftLeft(Arg1, 8))) }\n 100 | end; 101 | ``` 102 | 103 | If you only have 2 or 1 fan, delete FAN2 or FAN2 & FAN1, respectively. 104 | 105 | Then find for EmbeddedControl Field in your DSDT, search for EmbeddedControl in your DSDT and you will see something like this: 106 | 107 | ``` 108 | OperationRegion (EC81, EmbeddedControl, Zero, 0xFF) 109 | Field (EC81, ByteAcc, Lock, Preserve) 110 | { 111 | ... 112 | } 113 | ``` 114 | 115 | Add these in the Field method as first. As mentioned above delete extra values. e.g. delete FG10 and FG11 if you don't have two GPU fans. 116 | 117 | ``` 118 | Offset (0xD0), 119 | FC00, 8, 120 | FC01, 8, // CPU Fan Speed 121 | FG00, 8, 122 | FG01, 8, // GPU Fan0 Speed 123 | FG10, 8, 124 | FG11, 8, // GPU Fan1 Speed 125 | ``` 126 | Note: the second GPU fan (GPU Fan1) can be located at a different offset. 127 | You can try to find the offset by looking at the EC table. Under windows you can 128 | use [RWEverything](http://rweverything.com/download/). For Clevo P950HR the second 129 | GPU fan is located at `Offset (0xE0)`, so edit the above accordingly. 130 | 131 | Last, edit FakeSMC.kext/Contents/Info.plist, change the data in FNum from 00 to number of your fans. 132 | --------------------------------------------------------------------------------