Skip to content

Commit

Permalink
yanglint FEATURE enable multiline for linenoise
Browse files Browse the repository at this point in the history
Had to add a new 'cli' command to yanglint in interactive mode.
Although the Multiline is more suitable for interactive mode,
it creates a problem for tcl tests because it often adds ANSI
escape codes. So at the beginning of the tcl tests, multiline
is turned off.
  • Loading branch information
lePici committed Oct 7, 2024
1 parent 9b79197 commit 8811658
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 36 deletions.
5 changes: 4 additions & 1 deletion tests/yanglint/interactive/ly.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ stty columns 720
# default setup for every unit test
variable ly_setup {
spawn $TUT
# turn off linenoise multiline.
ly_skip_warnings
# Searchpath is set, so modules can be loaded via the 'load' command.
ly_cmd "searchpath $::env(YANG_MODULES_DIR)"
Expand All @@ -32,7 +33,9 @@ variable ly_cleanup {
# Skip no dir and/or no history warnings and prompt.
proc ly_skip_warnings {} {
global prompt
expect -re "(YANGLINT.*)*$prompt" {}
expect -re "$prompt" {}
send -- "cli -s\r"
expect -re "\n$prompt" {}
}

# Send command 'cmd' to the process, expect error header and then check output string by 'pattern'.
Expand Down
3 changes: 3 additions & 0 deletions tools/lint/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ set(lintsrc
if(ENABLE_YANGLINT_INTERACTIVE)
set(lintsrc ${lintsrc}
main.c
cmd_cli.c
completion.c
configuration.c
interactive_cmd.c
linenoise/linenoise.c
linenoise/utf8.c)
else()
set(lintsrc ${lintsrc}
interactive_cmd_off.c
main_ni_only.c)
endif()

Expand Down
37 changes: 36 additions & 1 deletion tools/lint/cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ typedef struct {
} COMMAND;

/**
* @brief The list of available commands.
* @brief The list of available commands for interactive and non-interactive mode.
*/
extern COMMAND commands[];

Expand All @@ -82,6 +82,18 @@ enum COMMAND_INDEX {
CMD_DEBUG
};

/**
* @brief The list of available commands only for interactive mode.
*/
extern COMMAND interactive_cmd[];

/**
* @brief Index for global variable ::interactive_cmd.
*/
enum COMMAND_I_INDEX {
CMD_CLI = 0
};

/**
* @brief For each cmd, call the COMMAND.free_func in the variable 'commands'.
*/
Expand Down Expand Up @@ -391,4 +403,27 @@ int cmd_debug_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
int cmd_debug_setlog(struct ly_ctx *ctx, struct yl_opt *yo);
void cmd_debug_help(void);

/* cmd_cli.c */

/**
* @brief Setup cli.
*
* @param[in,out] ctx context for libyang.
* @param[in,out] yo context for yanglint.
* @param[in] posv Name of the cli mode.
* @return 0 on success.
*/
int cmd_cli_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_cli_help(void);

/**
* @copydoc cmd_add_opt
*/
int cmd_cli_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);

/**
* @copydoc cmd_add_dep
*/
int cmd_cli_dep(struct yl_opt *yo, int posc);

#endif /* COMMANDS_H_ */
109 changes: 109 additions & 0 deletions tools/lint/cmd_cli.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* @file cli_verb.c
* @author Adam Piecek <[email protected]>
* @brief 'cli' command of the libyang's yanglint tool.
*
* Copyright (c) 2024-2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/

#include "cmd.h"

#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <strings.h>

#include "libyang.h"
#include "linenoise/linenoise.h"

#include "common.h"
#include "yl_opt.h"

#define LY_CLI_SINGLELINE 0x1

uint8_t cli_flags;

void
cmd_cli_help(void)
{
printf("Usage: cli [-s]\n"
" Settings for the command line interface.\n\n"
" -s, --toggle-singleline\n"
" The singleline settings toggles how the cli handles the overflow of typed text\n"
" across the screen. By default, it puts overflow text on a new line. But this\n"
" setting will make the text will scroll towards right to make room while\n"
" sending much less ANSI escape codes.\n");
}

int
cmd_cli_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"toggle-singleline", no_argument, NULL, 's'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};

if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
return rc;
}

while ((opt = getopt_long(argc, yo->argv, interactive_cmd[CMD_CLI].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 's':
yo->cli_flags |= LY_CLI_SINGLELINE;
break;
case 'h':
cmd_cli_help();
return 1;
default:
YLMSG_E("Unknown option.");
return 1;
}
}

*posv = &yo->argv[optind];
*posc = argc - optind;

return 0;
}

int
cmd_cli_dep(struct yl_opt *yo, int posc)
{
(void) yo;

if (posc != 0) {
YLMSG_E("Only options are expected.");
cmd_cli_help();
return 1;
}

return 0;
}

int
cmd_cli_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
{
(void) ctx, (void) posv;

if (yo->cli_flags & LY_CLI_SINGLELINE) {
if (cli_flags & LY_CLI_SINGLELINE) {
cli_flags &= ~LY_CLI_SINGLELINE;
linenoiseSetMultiLine(1);
} else {
cli_flags |= LY_CLI_SINGLELINE;
linenoiseSetMultiLine(0);
}
}

return 0;
}
12 changes: 12 additions & 0 deletions tools/lint/cmd_help.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ cmd_help_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
break;
}
}
/* interactive-only command */
for (uint16_t i = 0; interactive_cmd[i].name; i++) {
if (strcmp(posv, interactive_cmd[i].name) == 0) {
match = 1;
if (interactive_cmd[i].help_func != NULL) {
interactive_cmd[i].help_func();
} else {
printf("%s: %s\n", posv, interactive_cmd[i].helpstring);
}
break;
}
}
if (!match) {
/* if unknown command specified, print the list of commands */
printf("Unknown command \'%s\'\n", posv);
Expand Down
26 changes: 26 additions & 0 deletions tools/lint/interactive_cmd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @file interactive_cmd.c
* @author Adam Piecek <[email protected]>
* @brief libyang's yanglint tool general commands
*
* Copyright (c) 2024-2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/

#define _GNU_SOURCE

#include "cmd.h"

/* Also keep enum COMMAND_I_INDEX updated. */
COMMAND interactive_cmd[] = {
{
"cli", cmd_cli_opt, cmd_cli_dep, cmd_cli_exec, NULL, cmd_cli_help, NULL,
"Settings for the command line interface", "sh"
},
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};
21 changes: 21 additions & 0 deletions tools/lint/interactive_cmd_off.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @file iteractive_cmd_off.c
* @author Adam Piecek <[email protected]>
* @brief libyang's yanglint tool general commands
*
* Copyright (c) 2024-2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/

#define _GNU_SOURCE

#include "cmd.h"

COMMAND interactive_cmd[] = {
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};
94 changes: 60 additions & 34 deletions tools/lint/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,65 @@ struct ly_ctx *ctx = NULL;
/* main_ni.c */
int main_ni(int argc, char *argv[]);

/**
* @brief Find command and execute.
*
* @param[in] comm Array of commands.
* @param[in] cmdline Input from command line.
* @param[in] cmdlen Length of @p cmdline.
* @param[in,out] cmd_found Flag if command was found.
* @param[in,out] yo Context for yanglint.
*/
static void
execute_command(COMMAND *comm, char *cmdline, size_t cmdlen, ly_bool *cmd_found, struct yl_opt *yo)
{
int posc = 0, i, j;
char *empty = NULL;
char **posv = &empty;

if (*cmd_found) {
return;
} else {
*cmd_found = 0;
}

/* execute the command if any valid specified */
for (i = 0; comm[i].name; i++) {
if (strncmp(cmdline, comm[i].name, cmdlen) || (comm[i].name[cmdlen] != '\0')) {
continue;
}

*cmd_found = 1;
if (comm[i].opt_func && comm[i].opt_func(yo, cmdline, &posv, &posc)) {
break;
}
if (comm[i].dep_func && comm[i].dep_func(yo, posc)) {
break;
}
if (posc) {
for (j = 0; j < posc; j++) {
yo->last_one = (j + 1) == posc;
if (comm[i].exec_func(&ctx, yo, posv[j])) {
break;
}
}
} else {
comm[i].exec_func(&ctx, yo, NULL);
}
if (comm[i].fin_func) {
comm[i].fin_func(ctx, yo);
}

break;
}
}

int
main(int argc, char *argv[])
{
int cmdlen, posc, i, j;
int cmdlen;
struct yl_opt yo = {0};
char *empty = NULL, *cmdline;
char **posv;
char *cmdline;
uint8_t cmd_found;

if (argc > 1) {
Expand All @@ -55,6 +107,7 @@ main(int argc, char *argv[])
/* continue in interactive mode */
linenoiseSetCompletionCallback(complete_cmd);
linenoiseSetEncodingFunctions(linenoiseUtf8PrevCharLen, linenoiseUtf8NextCharLen, linenoiseUtf8ReadCode);
linenoiseSetMultiLine(1);
load_config();

if (ly_ctx_new(NULL, YL_DEFAULT_CTX_OPTIONS, &ctx)) {
Expand All @@ -65,9 +118,6 @@ main(int argc, char *argv[])
while (!done) {
cmd_found = 0;

posv = &empty;
posc = 0;

/* get the command from user */
cmdline = linenoise(PROMPT);

Expand All @@ -86,35 +136,11 @@ main(int argc, char *argv[])
/* isolate the command word. */
for (cmdlen = 0; cmdline[cmdlen] && (cmdline[cmdlen] != ' '); cmdlen++) {}

/* execute the command if any valid specified */
for (i = 0; commands[i].name; i++) {
if (strncmp(cmdline, commands[i].name, (size_t)cmdlen) || (commands[i].name[cmdlen] != '\0')) {
continue;
}
/* execute interactive or non-interactive command */
execute_command(commands, cmdline, cmdlen, &cmd_found, &yo);

cmd_found = 1;
if (commands[i].opt_func && commands[i].opt_func(&yo, cmdline, &posv, &posc)) {
break;
}
if (commands[i].dep_func && commands[i].dep_func(&yo, posc)) {
break;
}
if (posc) {
for (j = 0; j < posc; j++) {
yo.last_one = (j + 1) == posc;
if (commands[i].exec_func(&ctx, &yo, posv[j])) {
break;
}
}
} else {
commands[i].exec_func(&ctx, &yo, NULL);
}
if (commands[i].fin_func) {
commands[i].fin_func(ctx, &yo);
}

break;
}
/* execute only interactive command if command was not found */
execute_command(interactive_cmd, cmdline, cmdlen, &cmd_found, &yo);

if (!cmd_found) {
/* if unknown command specified, tell it to user */
Expand Down
Loading

0 comments on commit 8811658

Please sign in to comment.