Just a little clarification. No functional changes. Pull Request: https://projects.blender.org/blender/blender/pulls/136776
179 lines
5.0 KiB
C++
179 lines
5.0 KiB
C++
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*
|
|
* Generic CLI "--command" declarations.
|
|
*
|
|
* Duplicate Commands
|
|
* ==================
|
|
*
|
|
* When two or more commands share the same identifier, a warning is printed and both are disabled.
|
|
*
|
|
* This is done because command-line actions may be destructive so the down-side of running the
|
|
* wrong command could be severe. The reason this is not considered an error is we can't prevent
|
|
* it so easily, unlike operator ID's which may be longer, commands are typically short terms
|
|
* which wont necessarily include an add-ons identifier as a prefix for e.g.
|
|
* Further, an error would break loading add-ons who's primary is *not*
|
|
* necessarily to provide command-line access.
|
|
* An alternative solution could be to generate unique names (number them for example)
|
|
* but this isn't reliable as it would depend on it the order add-ons are loaded which
|
|
* isn't under user control.
|
|
*/
|
|
|
|
#include <iostream>
|
|
|
|
#include "BLI_vector.hh"
|
|
|
|
#include "BKE_blender_cli_command.hh" /* own include */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Internal API
|
|
* \{ */
|
|
|
|
using CommandHandlerPtr = std::unique_ptr<CommandHandler>;
|
|
|
|
/**
|
|
* All registered command handlers.
|
|
* \note the order doesn't matter as duplicates are detected and prevented from running.
|
|
*/
|
|
blender::Vector<CommandHandlerPtr> g_command_handlers;
|
|
|
|
static CommandHandler *blender_cli_command_lookup(const std::string &id)
|
|
{
|
|
for (CommandHandlerPtr &cmd_iter : g_command_handlers) {
|
|
if (id == cmd_iter->id) {
|
|
return cmd_iter.get();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static int blender_cli_command_index(const CommandHandler *cmd)
|
|
{
|
|
int index = 0;
|
|
for (CommandHandlerPtr &cmd_iter : g_command_handlers) {
|
|
if (cmd_iter.get() == cmd) {
|
|
return index;
|
|
}
|
|
index++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Public API
|
|
* \{ */
|
|
|
|
void BKE_blender_cli_command_register(std::unique_ptr<CommandHandler> cmd)
|
|
{
|
|
bool is_duplicate = false;
|
|
if (CommandHandler *cmd_exists = blender_cli_command_lookup(cmd->id)) {
|
|
std::cerr << "warning: registered duplicate command \"" << cmd->id
|
|
<< "\", this will be inaccessible" << std::endl;
|
|
cmd_exists->is_duplicate = true;
|
|
is_duplicate = true;
|
|
}
|
|
cmd->is_duplicate = is_duplicate;
|
|
g_command_handlers.append(std::move(cmd));
|
|
}
|
|
|
|
bool BKE_blender_cli_command_unregister(CommandHandler *cmd)
|
|
{
|
|
const int cmd_index = blender_cli_command_index(cmd);
|
|
if (cmd_index == -1) {
|
|
std::cerr << "failed to unregister command handler" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
/* Update duplicates after removal. */
|
|
if (cmd->is_duplicate) {
|
|
CommandHandler *cmd_other = nullptr;
|
|
for (CommandHandlerPtr &cmd_iter : g_command_handlers) {
|
|
/* Skip self. */
|
|
if (cmd == cmd_iter.get()) {
|
|
continue;
|
|
}
|
|
if (cmd_iter->is_duplicate && (cmd_iter->id == cmd->id)) {
|
|
if (cmd_other) {
|
|
/* Two or more found, clear and break. */
|
|
cmd_other = nullptr;
|
|
break;
|
|
}
|
|
cmd_other = cmd_iter.get();
|
|
}
|
|
}
|
|
if (cmd_other) {
|
|
cmd_other->is_duplicate = false;
|
|
}
|
|
}
|
|
|
|
g_command_handlers.remove_and_reorder(cmd_index);
|
|
|
|
return true;
|
|
}
|
|
|
|
int BKE_blender_cli_command_exec(bContext *C, const char *id, const int argc, const char **argv)
|
|
{
|
|
CommandHandler *cmd = blender_cli_command_lookup(id);
|
|
if (cmd == nullptr) {
|
|
std::cerr << "Unrecognized command: \"" << id << "\"" << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (cmd->is_duplicate) {
|
|
std::cerr << "Command: \"" << id
|
|
<< "\" was registered multiple times, must be resolved, aborting!" << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return cmd->exec(C, argc, argv);
|
|
}
|
|
|
|
void BKE_blender_cli_command_print_help()
|
|
{
|
|
/* As `g_command_handlers` isn't ordered, sorting in-place is acceptable. */
|
|
std::sort(g_command_handlers.begin(),
|
|
g_command_handlers.end(),
|
|
[](const CommandHandlerPtr &a, const CommandHandlerPtr &b) { return a->id < b->id; });
|
|
|
|
for (int pass = 0; pass < 2; pass++) {
|
|
std::cout << ((pass == 0) ? "Blender Command Listing:" :
|
|
"Duplicate Command Listing (ignored):")
|
|
<< std::endl;
|
|
|
|
const bool show_duplicates = pass > 0;
|
|
bool found = false;
|
|
bool has_duplicate = false;
|
|
for (CommandHandlerPtr &cmd_iter : g_command_handlers) {
|
|
if (cmd_iter->is_duplicate) {
|
|
has_duplicate = true;
|
|
}
|
|
if (cmd_iter->is_duplicate != show_duplicates) {
|
|
continue;
|
|
}
|
|
|
|
std::cout << "\t" << cmd_iter->id << std::endl;
|
|
found = true;
|
|
}
|
|
|
|
if (!found) {
|
|
std::cout << "\tNone found" << std::endl;
|
|
}
|
|
/* Don't print that no duplicates are found as it's not helpful. */
|
|
if (pass == 0 && !has_duplicate) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BKE_blender_cli_command_free_all()
|
|
{
|
|
g_command_handlers.clear();
|
|
}
|
|
|
|
/** \} */
|