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