| /* |
| * Copyright 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "test/headless/get_options.h" |
| |
| #include <base/logging.h> |
| #include <getopt.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| |
| #include <list> |
| #include <string> |
| |
| #include "gd/os/log.h" |
| #include "types/bluetooth/uuid.h" |
| #include "types/raw_address.h" |
| |
| namespace { |
| enum OptionType { |
| kOptionDevice = 0, |
| kOptionLoop = 1, |
| kOptionUuid = 2, |
| kOptionMsleep = 3, |
| kOptionStdErr = 4, |
| kOptionFlags = 5, |
| kOptionClear = 6, |
| }; |
| |
| constexpr struct option long_options[] = { |
| {"device", required_argument, 0, 0}, // kOptionDevice |
| {"loop", required_argument, 0, 0}, // kOptionLoop/ |
| {"uuid", required_argument, 0, 0}, // kOptionUuid |
| {"msleep", required_argument, 0, 0}, // kOptionMsleep |
| {"stderr", no_argument, 0, 0}, // kOptionStdErr |
| {"flags", required_argument, 0, 0}, // kOptionFlags |
| {"clear", no_argument, 0, 0}, // kOptionDevice |
| {0, 0, 0, 0}}; |
| |
| const char* kShortArgs = "cd:l:u:"; |
| |
| } // namespace |
| |
| void bluetooth::test::headless::GetOpt::Usage() const { |
| fprintf(stdout, "%s: Usage:\n", name_); |
| fprintf(stdout, "%s -c Clear logcat logs\n", name_); |
| fprintf(stdout, |
| "%s --device=<device,> Comma separated list of remote devices\n", |
| name_); |
| fprintf(stdout, |
| "%s --flags=<flags,> Comma separated list of gd init flags\n", |
| name_); |
| fprintf(stdout, "%s --uuid=<uuid,> Comma separated list of uuids\n", |
| name_); |
| fprintf(stdout, "%s --loop=<loop> Number of loops\n", name_); |
| fprintf(stdout, "%s --msleep=<msecs> Sleep msec between loops\n", name_); |
| fprintf(stdout, "%s --stderr Dump stderr to stdout\n", name_); |
| fflush(nullptr); |
| } |
| |
| void bluetooth::test::headless::GetOpt::ParseValue( |
| char* optarg, std::list<std::string>& string_list) { |
| CHECK(optarg != nullptr); |
| char* p = optarg; |
| char* pp = optarg; |
| while (*p != '\0') { |
| if (*p == ',') { |
| *p = 0; |
| string_list.push_back(std::string(pp)); |
| pp = p + 1; |
| } |
| p++; |
| } |
| if (pp != p) string_list.push_back(std::string(pp)); |
| } |
| |
| std::vector<std::string> bluetooth::test::headless::GetOpt::Split( |
| std::string s) { |
| std::stringstream ss(s); |
| std::vector<std::string> values; |
| std::string item; |
| while (std::getline(ss, item, '=')) { |
| values.push_back(item); |
| } |
| return values; |
| } |
| |
| void bluetooth::test::headless::GetOpt::ProcessOption(int option_index, |
| char* optarg) { |
| std::list<std::string> string_list; |
| OptionType option_type = static_cast<OptionType>(option_index); |
| |
| switch (option_type) { |
| case kOptionDevice: |
| if (!optarg) return; |
| ParseValue(optarg, string_list); |
| for (auto& entry : string_list) { |
| if (RawAddress::IsValidAddress(entry)) { |
| RawAddress address; |
| RawAddress::FromString(entry, address); |
| device_.push_back(address); |
| } |
| } |
| break; |
| case kOptionLoop: |
| loop_ = std::stoul(optarg, nullptr, 0); |
| break; |
| case kOptionUuid: |
| if (!optarg) return; |
| ParseValue(optarg, string_list); |
| for (auto& entry : string_list) { |
| uuid_.push_back( |
| bluetooth::Uuid::From16Bit(std::stoul(entry.c_str(), nullptr, 0))); |
| } |
| break; |
| case kOptionMsleep: |
| if (!optarg) return; |
| msec_ = std::stoul(optarg, nullptr, 0); |
| break; |
| case kOptionStdErr: |
| close_stderr_ = false; |
| break; |
| case kOptionFlags: |
| if (!optarg) return; |
| ParseValue(optarg, string_list); |
| for (auto& flag : string_list) { |
| init_flags_.push_back(flag); |
| } |
| break; |
| case kOptionClear: |
| clear_logcat_ = true; |
| break; |
| default: |
| fflush(nullptr); |
| valid_ = false; |
| return; |
| break; |
| } |
| } |
| |
| void bluetooth::test::headless::GetOpt::ParseStackInitFlags() { |
| if (init_flags_.size() == 0) return; |
| |
| ASSERT(stack_init_flags_ == nullptr); |
| |
| unsigned idx = 0; |
| stack_init_flags_ = (const char**)calloc(sizeof(char*), init_flags_.size()); |
| for (const std::string& flag : init_flags_) |
| stack_init_flags_[idx++] = flag.c_str(); |
| stack_init_flags_[idx] = nullptr; |
| } |
| |
| const char** bluetooth::test::headless::GetOpt::StackInitFlags() const { |
| return stack_init_flags_; |
| } |
| |
| bluetooth::test::headless::GetOpt::GetOpt(int argc, char** argv) |
| : name_(argv[0]) { |
| while (1) { |
| int option_index = 0; |
| int c = |
| getopt_long_only(argc, argv, kShortArgs, long_options, &option_index); |
| if (c == -1) break; |
| |
| switch (c) { |
| case 0: |
| ProcessOption(static_cast<OptionType>(option_index), optarg); |
| break; |
| case '?': |
| Usage(); |
| valid_ = false; |
| return; |
| default: |
| printf("?? getopt returned character code 0%o ??\n", c); |
| } |
| } |
| |
| while (optind < argc) { |
| non_options_.push_back(argv[optind++]); |
| } |
| |
| ParseStackInitFlags(); |
| |
| fflush(nullptr); |
| } |
| |
| bluetooth::test::headless::GetOpt::~GetOpt() { free(stack_init_flags_); } |