├── ClevoControl.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcuserdata
│ └── datasone.xcuserdatad
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── ClevoControl.xcscheme
└── project.pbxproj
├── ECCtrl.h
├── ClevoControl
├── ClevoControl.hpp
├── Info.plist
└── ClevoControl.cpp
├── LICENSE
├── README.md
└── ClevoKBFanControl
├── main.cpp
└── args.hxx
/ClevoControl.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------