1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <ctype.h>
7 #include "shell_ops.h"
8 #include "shell_help.h"
9 #include "shell_utils.h"
10
11
12 /* Function prints a string on terminal screen with requested margin.
13 * It takes care to not divide words.
14 * shell Pointer to shell instance.
15 * p_str Pointer to string to be printed.
16 * terminal_offset Requested left margin.
17 * offset_first_line Add margin to the first printed line.
18 */
formatted_text_print(const struct shell * sh,const char * str,size_t terminal_offset,bool offset_first_line)19 static void formatted_text_print(const struct shell *sh, const char *str,
20 size_t terminal_offset, bool offset_first_line)
21 {
22 size_t offset = 0;
23 size_t length;
24
25 if (str == NULL) {
26 return;
27 }
28
29 if (offset_first_line) {
30 z_shell_op_cursor_horiz_move(sh, terminal_offset);
31 }
32
33
34 /* Skipping whitespace. */
35 while (isspace((int) *(str + offset)) != 0) {
36 ++offset;
37 }
38
39 while (true) {
40 size_t idx = 0;
41 bool newline_found = false;
42
43 length = z_shell_strlen(str) - offset;
44
45 if (length <=
46 sh->ctx->vt100_ctx.cons.terminal_wid - terminal_offset) {
47 for (idx = 0; idx < length; idx++) {
48 if (*(str + offset + idx) == '\n') {
49 z_transport_buffer_flush(sh);
50 z_shell_write(sh, str + offset, idx);
51 offset += idx + 1;
52 z_cursor_next_line_move(sh);
53 z_shell_op_cursor_horiz_move(sh,
54 terminal_offset);
55 newline_found = true;
56 break;
57 }
58 }
59
60 /* If we found a newline, continue processing the remaining text */
61 if (newline_found) {
62 continue;
63 }
64
65 /* String will fit in one line. */
66 z_shell_raw_fprintf(sh->fprintf_ctx, str + offset);
67
68 break;
69 }
70
71 /* String is longer than terminal line so text needs to
72 * divide in the way to not divide words.
73 */
74 length = sh->ctx->vt100_ctx.cons.terminal_wid
75 - terminal_offset;
76
77 while (true) {
78 /* Determining line break. */
79 if (isspace((int) (*(str + offset + idx))) != 0) {
80 length = idx;
81 if (*(str + offset + idx) == '\n') {
82 break;
83 }
84 }
85
86 if ((idx + terminal_offset) >=
87 sh->ctx->vt100_ctx.cons.terminal_wid) {
88 /* End of line reached. */
89 break;
90 }
91
92 ++idx;
93 }
94
95 /* Writing one line, fprintf IO buffer must be flushed
96 * before calling shell_write.
97 */
98 z_transport_buffer_flush(sh);
99 z_shell_write(sh, str + offset, length);
100 offset += length;
101
102 /* Calculating text offset to ensure that next line will
103 * not begin with a space.
104 */
105 while (isspace((int) (*(str + offset))) != 0) {
106 ++offset;
107 }
108
109 z_cursor_next_line_move(sh);
110 z_shell_op_cursor_horiz_move(sh, terminal_offset);
111
112 }
113 z_cursor_next_line_move(sh);
114 }
115
formatted_structured_help_print(const struct shell * sh,const char * item_name,const char * item_help,size_t terminal_offset)116 static void formatted_structured_help_print(const struct shell *sh, const char *item_name,
117 const char *item_help, size_t terminal_offset)
118 {
119 const struct shell_cmd_help *structured = (const struct shell_cmd_help *)item_help;
120
121 if (structured->description) {
122 formatted_text_print(sh, structured->description, terminal_offset, false);
123 }
124
125 if (structured->usage) {
126 z_shell_op_cursor_horiz_move(sh, terminal_offset);
127 z_shell_fprintf(sh, SHELL_NORMAL, "Usage: %s ", item_name);
128 formatted_text_print(sh, structured->usage, terminal_offset + 7, false);
129 }
130 }
131
help_item_print(const struct shell * sh,const char * item_name,uint16_t item_name_width,const char * item_help)132 static void help_item_print(const struct shell *sh, const char *item_name,
133 uint16_t item_name_width, const char *item_help)
134 {
135 static const uint8_t tabulator[] = " ";
136 static const char sub_cmd_sep[] = ": "; /* subcommands separator */
137 const uint16_t offset = 2 * strlen(tabulator) + item_name_width + strlen(sub_cmd_sep);
138
139 if ((item_name == NULL) || (item_name[0] == '\0')) {
140 return;
141 }
142
143 if (!IS_ENABLED(CONFIG_NEWLIB_LIBC) &&
144 !IS_ENABLED(CONFIG_ARCH_POSIX)) {
145 /* print option name */
146 z_shell_fprintf(sh, SHELL_NORMAL, "%s%-*s", tabulator,
147 item_name_width, item_name);
148 } else {
149 uint16_t tmp = item_name_width - strlen(item_name);
150 char space = ' ';
151
152 z_shell_fprintf(sh, SHELL_NORMAL, "%s%s", tabulator,
153 item_name);
154
155 if (item_help) {
156 for (uint16_t i = 0; i < tmp; i++) {
157 z_shell_write(sh, &space, 1);
158 }
159 }
160 }
161
162 if (item_help == NULL) {
163 z_cursor_next_line_move(sh);
164 return;
165 } else {
166 z_shell_fprintf(sh, SHELL_NORMAL, "%s: ", tabulator);
167 }
168 /* print option help */
169 if (shell_help_is_structured(item_help)) {
170 formatted_structured_help_print(sh, item_name, item_help, offset);
171 } else {
172 formatted_text_print(sh, item_help, offset, false);
173 }
174 }
175
176 /* Function prints all subcommands of the parent command together with their
177 * help string
178 */
z_shell_help_subcmd_print(const struct shell * sh,const struct shell_static_entry * parent,const char * description)179 void z_shell_help_subcmd_print(const struct shell *sh,
180 const struct shell_static_entry *parent,
181 const char *description)
182 {
183 const struct shell_static_entry *entry = NULL;
184 struct shell_static_entry dloc;
185 uint16_t longest = 0U;
186 size_t idx = 0;
187
188 /* Searching for the longest subcommand to print. */
189 while ((entry = z_shell_cmd_get(parent, idx++, &dloc)) != NULL) {
190 longest = Z_MAX(longest, z_shell_strlen(entry->syntax));
191 }
192
193 /* No help to print */
194 if (longest == 0) {
195 return;
196 }
197
198 if (description != NULL) {
199 z_shell_fprintf(sh, SHELL_NORMAL, description);
200 }
201
202 /* Printing subcommands and help string (if exists). */
203 idx = 0;
204
205 while ((entry = z_shell_cmd_get(parent, idx++, &dloc)) != NULL) {
206 help_item_print(sh, entry->syntax, longest, entry->help);
207 }
208 }
209
z_shell_help_cmd_print(const struct shell * sh,const struct shell_static_entry * cmd)210 void z_shell_help_cmd_print(const struct shell *sh,
211 const struct shell_static_entry *cmd)
212 {
213 static const char cmd_sep[] = " - "; /* commands separator */
214 uint16_t field_width;
215
216 field_width = z_shell_strlen(cmd->syntax) + z_shell_strlen(cmd_sep);
217
218 z_shell_fprintf(sh, SHELL_NORMAL, "%s%s", cmd->syntax, cmd_sep);
219
220 if (shell_help_is_structured(cmd->help)) {
221 formatted_structured_help_print(sh, cmd->syntax, cmd->help, field_width);
222 } else {
223 formatted_text_print(sh, cmd->help, field_width, false);
224 }
225 }
226
z_shell_help_request(const char * str)227 bool z_shell_help_request(const char *str)
228 {
229 if (!IS_ENABLED(CONFIG_SHELL_HELP_OPT_PARSE)) {
230 return false;
231 }
232
233 if (!strcmp(str, "-h") || !strcmp(str, "--help")) {
234 return true;
235 }
236
237 return false;
238 }
239