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 
42 		length = z_shell_strlen(str) - offset;
43 
44 		if (length <=
45 		    sh->ctx->vt100_ctx.cons.terminal_wid - terminal_offset) {
46 			for (idx = 0; idx < length; idx++) {
47 				if (*(str + offset + idx) == '\n') {
48 					z_transport_buffer_flush(sh);
49 					z_shell_write(sh, str + offset, idx);
50 					offset += idx + 1;
51 					z_cursor_next_line_move(sh);
52 					z_shell_op_cursor_horiz_move(sh,
53 							terminal_offset);
54 					break;
55 				}
56 			}
57 
58 			/* String will fit in one line. */
59 			z_shell_raw_fprintf(sh->fprintf_ctx, str + offset);
60 
61 			break;
62 		}
63 
64 		/* String is longer than terminal line so text needs to
65 		 * divide in the way to not divide words.
66 		 */
67 		length = sh->ctx->vt100_ctx.cons.terminal_wid
68 				- terminal_offset;
69 
70 		while (true) {
71 			/* Determining line break. */
72 			if (isspace((int) (*(str + offset + idx))) != 0) {
73 				length = idx;
74 				if (*(str + offset + idx) == '\n') {
75 					break;
76 				}
77 			}
78 
79 			if ((idx + terminal_offset) >=
80 			    sh->ctx->vt100_ctx.cons.terminal_wid) {
81 				/* End of line reached. */
82 				break;
83 			}
84 
85 			++idx;
86 		}
87 
88 		/* Writing one line, fprintf IO buffer must be flushed
89 		 * before calling shell_write.
90 		 */
91 		z_transport_buffer_flush(sh);
92 		z_shell_write(sh, str + offset, length);
93 		offset += length;
94 
95 		/* Calculating text offset to ensure that next line will
96 		 * not begin with a space.
97 		 */
98 		while (isspace((int) (*(str + offset))) != 0) {
99 			++offset;
100 		}
101 
102 		z_cursor_next_line_move(sh);
103 		z_shell_op_cursor_horiz_move(sh, terminal_offset);
104 
105 	}
106 	z_cursor_next_line_move(sh);
107 }
108 
help_item_print(const struct shell * sh,const char * item_name,uint16_t item_name_width,const char * item_help)109 static void help_item_print(const struct shell *sh, const char *item_name,
110 			    uint16_t item_name_width, const char *item_help)
111 {
112 	static const uint8_t tabulator[] = "  ";
113 	const uint16_t offset = 2 * strlen(tabulator) + item_name_width + 1;
114 
115 	if ((item_name == NULL) || (item_name[0] == '\0')) {
116 		return;
117 	}
118 
119 	if (!IS_ENABLED(CONFIG_NEWLIB_LIBC) &&
120 	    !IS_ENABLED(CONFIG_ARCH_POSIX)) {
121 		/* print option name */
122 		z_shell_fprintf(sh, SHELL_NORMAL, "%s%-*s", tabulator,
123 				item_name_width, item_name);
124 	} else {
125 		uint16_t tmp = item_name_width - strlen(item_name);
126 		char space = ' ';
127 
128 		z_shell_fprintf(sh, SHELL_NORMAL, "%s%s", tabulator,
129 				item_name);
130 
131 		if (item_help) {
132 			for (uint16_t i = 0; i < tmp; i++) {
133 				z_shell_write(sh, &space, 1);
134 			}
135 		}
136 	}
137 
138 	if (item_help == NULL) {
139 		z_cursor_next_line_move(sh);
140 		return;
141 	} else {
142 		z_shell_fprintf(sh, SHELL_NORMAL, "%s: ", tabulator);
143 	}
144 	/* print option help */
145 	formatted_text_print(sh, item_help, offset, false);
146 }
147 
148 /* Function prints all subcommands of the parent command together with their
149  * help string
150  */
z_shell_help_subcmd_print(const struct shell * sh,const struct shell_static_entry * parent,const char * description)151 void z_shell_help_subcmd_print(const struct shell *sh,
152 			       const struct shell_static_entry *parent,
153 			       const char *description)
154 {
155 	const struct shell_static_entry *entry = NULL;
156 	struct shell_static_entry dloc;
157 	uint16_t longest = 0U;
158 	size_t idx = 0;
159 
160 	/* Searching for the longest subcommand to print. */
161 	while ((entry = z_shell_cmd_get(parent, idx++, &dloc)) != NULL) {
162 		longest = Z_MAX(longest, z_shell_strlen(entry->syntax));
163 	}
164 
165 	/* No help to print */
166 	if (longest == 0) {
167 		return;
168 	}
169 
170 	if (description != NULL) {
171 		z_shell_fprintf(sh, SHELL_NORMAL, description);
172 	}
173 
174 	/* Printing subcommands and help string (if exists). */
175 	idx = 0;
176 
177 	while ((entry = z_shell_cmd_get(parent, idx++, &dloc)) != NULL) {
178 		help_item_print(sh, entry->syntax, longest, entry->help);
179 	}
180 }
181 
z_shell_help_cmd_print(const struct shell * sh,const struct shell_static_entry * cmd)182 void z_shell_help_cmd_print(const struct shell *sh,
183 			    const struct shell_static_entry *cmd)
184 {
185 	static const char cmd_sep[] = " - "; /* commands separator */
186 	uint16_t field_width;
187 
188 	field_width = z_shell_strlen(cmd->syntax) + z_shell_strlen(cmd_sep);
189 
190 	z_shell_fprintf(sh, SHELL_NORMAL, "%s%s", cmd->syntax, cmd_sep);
191 
192 	formatted_text_print(sh, cmd->help, field_width, false);
193 }
194 
z_shell_help_request(const char * str)195 bool z_shell_help_request(const char *str)
196 {
197 	if (!IS_ENABLED(CONFIG_SHELL_HELP_OPT_PARSE)) {
198 		return false;
199 	}
200 
201 	if (!strcmp(str, "-h") || !strcmp(str, "--help")) {
202 		return true;
203 	}
204 
205 	return false;
206 }
207