-D flag changed to -l flag. It now expects a string. (DEBUG, INFO etc)

-v flag now prints the cli version

new flag -e prints extra console info

new flag -f, prevents the CLI from splitting strings on spaces. This allows users to pass string requests containing spaces (setting devices, naming channel labels etc)
This commit is contained in:
onyx-and-iris 2025-12-02 02:09:25 +00:00
parent 12522667d3
commit ca15785789
3 changed files with 69 additions and 26 deletions

View File

@ -15,6 +15,7 @@ struct quickcommand
}; };
void remove_last_part_of_path(char *fullpath); void remove_last_part_of_path(char *fullpath);
int log_level_from_string(const char *level);
char *kind_as_string(char *s, int kind, int n); char *kind_as_string(char *s, int kind, int n);
char *version_as_string(char *s, long v, int n); char *version_as_string(char *s, long v, int n);
bool is_comment(char *s); bool is_comment(char *s);

View File

@ -12,6 +12,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "util.h" #include "util.h"
#include "log.h"
/** /**
* @brief Removes the last part of a path * @brief Removes the last part of a path
@ -28,6 +29,29 @@ void remove_last_part_of_path(char *fullpath)
} }
} }
/**
* @brief Gets log level as int from string
* @param level Log level as string
* @return int Log level as int, or -1 if not found
*/
int log_level_from_string(const char *level)
{
if (strcmp(level, "TRACE") == 0)
return LOG_TRACE;
else if (strcmp(level, "DEBUG") == 0)
return LOG_DEBUG;
else if (strcmp(level, "INFO") == 0)
return LOG_INFO;
else if (strcmp(level, "WARN") == 0)
return LOG_WARN;
else if (strcmp(level, "ERROR") == 0)
return LOG_ERROR;
else if (strcmp(level, "FATAL") == 0)
return LOG_FATAL;
else
return -1;
}
/** /**
* @brief Converts Voicemeeter's kind into a string. * @brief Converts Voicemeeter's kind into a string.
* *

View File

@ -18,21 +18,24 @@
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
#define USAGE "Usage: .\\vmrcli.exe [-h] [-i|-I] [-k] [-D] [-v] [-c] [-m] [-s] <api commands>\n" \ #define USAGE "Usage: .\\vmrcli.exe [-h] [-v] [-i|-I] [-f] [-k] [-l] [-e] [-c] [-m] [-s] <api commands>\n" \
"Where: \n" \ "Where: \n" \
"\th: Prints the help message\n" \ "\th: Print the help message\n" \
"\tv: Print the version number\n" \
"\ti: Enable interactive mode, use (-I) to disable the '>>' prompt\n" \ "\ti: Enable interactive mode, use (-I) to disable the '>>' prompt\n" \
"\tf: Do not split input on spaces\n" \
"\tk: The kind of Voicemeeter (basic, banana, potato)\n" \ "\tk: The kind of Voicemeeter (basic, banana, potato)\n" \
"\tD: Set log level 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=FATAL\n" \ "\tl: Set log level, must be one of TRACE, DEBUG, INFO, WARN, ERROR, or FATAL\n" \
"\tv: Enable extra console output (toggle, set messages)\n" \ "\te: Enable extra console output (toggle, set messages)\n" \
"\tc: Load a user configuration (give the full file path)\n" \ "\tc: Load a user configuration (give the full file path)\n" \
"\tm: Launch the MacroButtons application\n" \ "\tm: Launch the MacroButtons application\n" \
"\ts: Launch the StreamerView application" "\ts: Launch the StreamerView application"
#define OPTSTR ":hk:msc:iID:v" #define OPTSTR ":hvk:msc:iIfl:e"
#define MAX_LINE 4096 /* Size of the input buffer */ #define MAX_LINE 4096 /* Size of the input buffer */
#define RES_SZ 512 /* Size of the buffer passed to VBVMR_GetParameterStringW */ #define RES_SZ 512 /* Size of the buffer passed to VBVMR_GetParameterStringW */
#define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) #define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
#define DELIMITERS " \t;," #define DELIMITERS " \t;,"
#define VERSION "0.12.0"
/** /**
* @enum The kind of values a get call may return. * @enum The kind of values a get call may return.
@ -58,13 +61,13 @@ struct result
} val; } val;
}; };
static bool vflag = false; static bool eflag = false;
static void terminate(PT_VMR vmr, char *msg); static void terminate(PT_VMR vmr, char *msg);
static void usage(); static void usage();
static enum kind set_kind(char *kval); static enum kind set_kind(char *kval);
static void interactive(PT_VMR vmr, bool with_prompt); static void interactive(PT_VMR vmr, bool with_prompt, char *delimiters);
static void parse_input(PT_VMR vmr, char *input); static void parse_input(PT_VMR vmr, char *input, char *delimiters);
static void parse_command(PT_VMR vmr, char *command); static void parse_command(PT_VMR vmr, char *command);
static void get(PT_VMR vmr, char *command, struct result *res); static void get(PT_VMR vmr, char *command, struct result *res);
@ -74,9 +77,10 @@ int main(int argc, char *argv[])
mflag = false, mflag = false,
sflag = false, sflag = false,
cflag = false, cflag = false,
fflag = false,
with_prompt = true; with_prompt = true;
int opt; int opt;
int dvalue; int log_level = LOG_WARN;
char *cvalue; char *cvalue;
enum kind kind = BANANAX64; enum kind kind = BANANAX64;
@ -92,6 +96,9 @@ int main(int argc, char *argv[])
{ {
switch (opt) switch (opt)
{ {
case 'v':
printf("vmrcli version %s\n", VERSION);
exit(EXIT_SUCCESS);
case 'k': case 'k':
kind = set_kind(optarg); kind = set_kind(optarg);
if (kind == UNKNOWN) if (kind == UNKNOWN)
@ -116,21 +123,24 @@ int main(int argc, char *argv[])
case 'i': case 'i':
iflag = true; iflag = true;
break; break;
case 'D': case 'f':
dvalue = atoi(optarg); fflag = true;
if (dvalue >= LOG_TRACE && dvalue <= LOG_FATAL) break;
case 'l':
log_level = log_level_from_string(optarg);
if (log_level != -1)
{ {
log_set_level(dvalue); log_set_level(log_level);
} }
else else
{ {
log_warn( log_warn(
"-D arg out of range, expected value from 0 up to 5\n" "-l arg out of range, expected TRACE, DEBUG, INFO, WARN, ERROR, or FATAL\n"
"Log level will default to LOG_WARN (3).\n"); "Log level will default to LOG_WARN (3).\n");
} }
break; break;
case 'v': case 'e':
vflag = true; eflag = true;
break; break;
case '?': case '?':
log_fatal("unknown option -- '%c'\n" log_fatal("unknown option -- '%c'\n"
@ -184,16 +194,22 @@ int main(int argc, char *argv[])
clear(vmr, is_pdirty); clear(vmr, is_pdirty);
} }
char *delimiter_ptr = DELIMITERS;
if (fflag)
{
delimiter_ptr++; /* skip space delimiter */
}
if (iflag) if (iflag)
{ {
puts("Interactive mode enabled. Enter 'Q' to exit."); puts("Interactive mode enabled. Enter 'Q' to exit.");
interactive(vmr, with_prompt); interactive(vmr, with_prompt, delimiter_ptr);
} }
else else
{ {
for (int i = optind; i < argc; ++i) for (int i = optind; i < argc; ++i)
{ {
parse_input(vmr, argv[i]); parse_input(vmr, argv[i], delimiter_ptr);
} }
} }
@ -256,8 +272,9 @@ static enum kind set_kind(char *kval)
* *
* @param vmr Pointer to the iVMR interface * @param vmr Pointer to the iVMR interface
* @param with_prompt If true, prints the interactive prompt '>>' * @param with_prompt If true, prints the interactive prompt '>>'
* @param delimiters A string of delimiter characters to split each input line
*/ */
static void interactive(PT_VMR vmr, bool with_prompt) static void interactive(PT_VMR vmr, bool with_prompt, char *delimiters)
{ {
char input[MAX_LINE]; char input[MAX_LINE];
size_t len; size_t len;
@ -270,7 +287,7 @@ static void interactive(PT_VMR vmr, bool with_prompt)
if (len == 1 && toupper(input[0]) == 'Q') if (len == 1 && toupper(input[0]) == 'Q')
break; break;
parse_input(vmr, input); parse_input(vmr, input, delimiters);
if (with_prompt) if (with_prompt)
printf(">> "); printf(">> ");
@ -284,19 +301,20 @@ static void interactive(PT_VMR vmr, bool with_prompt)
* *
* @param vmr Pointer to the iVMR interface * @param vmr Pointer to the iVMR interface
* @param input Each input line, from stdin or CLI args * @param input Each input line, from stdin or CLI args
* @param delimiters A string of delimiter characters to split each input line
*/ */
static void parse_input(PT_VMR vmr, char *input) static void parse_input(PT_VMR vmr, char *input, char *delimiters)
{ {
if (is_comment(input)) if (is_comment(input))
return; return;
char *token, *p; char *token, *p;
token = strtok_r(input, DELIMITERS, &p); token = strtok_r(input, delimiters, &p);
while (token != NULL) while (token != NULL)
{ {
parse_command(vmr, token); parse_command(vmr, token);
token = strtok_r(NULL, DELIMITERS, &p); token = strtok_r(NULL, delimiters, &p);
} }
} }
@ -323,7 +341,7 @@ static void parse_command(PT_VMR vmr, char *command)
if (qc_ptr != NULL) if (qc_ptr != NULL)
{ {
set_parameters(vmr, qc_ptr->fullcommand); set_parameters(vmr, qc_ptr->fullcommand);
if (vflag) if (eflag)
{ {
printf("Setting %s\n", qc_ptr->fullcommand); printf("Setting %s\n", qc_ptr->fullcommand);
} }
@ -341,7 +359,7 @@ static void parse_command(PT_VMR vmr, char *command)
if (res.val.f == 1 || res.val.f == 0) if (res.val.f == 1 || res.val.f == 0)
{ {
set_parameter_float(vmr, command, 1 - res.val.f); set_parameter_float(vmr, command, 1 - res.val.f);
if (vflag) if (eflag)
{ {
printf("Toggling %s\n", command); printf("Toggling %s\n", command);
} }
@ -355,7 +373,7 @@ static void parse_command(PT_VMR vmr, char *command)
if (strchr(command, '=') != NULL) /* set */ if (strchr(command, '=') != NULL) /* set */
{ {
set_parameters(vmr, command); set_parameters(vmr, command);
if (vflag) if (eflag)
{ {
printf("Setting %s\n", command); printf("Setting %s\n", command);
} }