C-Menu
0.2.9
A User Interface Toolkit
Toggle main menu visibility
Loading...
Searching...
No Matches
parse_menu_desc.c
Go to the documentation of this file.
1
/** @file parse_menu_desc.c
2
@brief Parse menu description file and create Menu
3
@author Bill Waller
4
Copyright (c) 2025
5
MIT License
6
billxwaller@gmail.com
7
@date 2026-02-09
8
*/
9
10
/** @defgroup parse_menu Menu Parser
11
@brief Functions for parsing menu description files and creating Menu
12
structures
13
*/
14
15
#
include
<
common
.
h
>
16
#
include
<
ctype
.
h
>
17
#
include
<
stddef
.
h
>
18
#
include
<
stdlib
.
h
>
19
#
include
<
string
.
h
>
20
21
unsigned
int
parse_menu_description
(Init *);
22
unsigned
int
get_command_type
(
char
*);
23
24
/** @brief Parse menu description file and create Menu
25
@ingroup parse_menu
26
@param init Pointer to Init structure containing menu information
27
@return 0 on success, non-zero on failure
28
*/
29
unsigned
int
parse_menu_description
(Init *init) {
30
FILE *fp;
31
char
tmp_str[
MAXLEN
];
32
char
tmp_buf[
MAXLEN
];
33
char
in_buf[
MAXLEN
];
34
char
*in_buf_p;
35
unsigned
char
ltr;
36
bool fltr[127];
37
int
directive;
38
int
l;
39
char
*s;
40
int
commands = 0;
41
int
choices = 0;
42
int
in_fp_line = 0;
43
menu
= init
->
menu
;
44
fp = fopen(
menu
->
mapp_spec
,
"r"
);
45
if
(fp ==
nullptr
) {
46
strnz__cpy
(
tmp_buf
,
"file not found"
,
MAXLEN
)
;
47
abend
(
-1
,
tmp_buf
)
;
48
exit(-1);
49
}
50
while
((fgets(in_buf,
MAXLEN
, fp)) !=
nullptr
) {
51
if
(in_buf[0] ==
'\0'
)
52
continue
;
53
in_fp_line++;
54
// Replace carriage returns, newlines, and tabs in in_buf with null
55
// terminators and spaces
56
chrep
(
in_buf
,
'\r'
,
'\0'
)
;
57
chrep
(
in_buf
,
'\n'
,
'\0'
)
;
58
chrep
(
in_buf
,
'\t'
,
' '
)
;
59
in_buf_p = in_buf;
60
directive = *in_buf_p;
61
in_buf_p++;
62
63
strnz__cpy
(
tmp_buf
,
in_buf_p
,
MAXLEN
)
;
64
trim
(
tmp_buf
)
;
65
l = strlen(tmp_buf);
66
if
(l == 0)
67
continue
;
68
if
(directive ==
'#'
)
69
continue
;
70
for
(ltr = 0; ltr < 32; ltr++)
71
fltr[ltr] = true;
72
for
(ltr = 32; ltr < 127; ltr++)
73
fltr[ltr] = false;
74
fltr[
'q'
] = true;
75
switch
(directive) {
76
/** '!' Command */
77
case
'!'
:
78
if
(!
menu
->
line_idx
)
79
break
;
80
if
(
menu
->
line
[
menu
->
line_idx
- 1]
->
type
!=
MT_TEXT
)
81
break
;
82
83
// Convert tmp_buf to command_str and determine command_type
84
menu
->
line_idx
--;
85
menu
->
line
[
menu
->
line_idx
]
->
command_str
= strdup(tmp_buf);
86
menu
->
line
[
menu
->
line_idx
]
->
command_type
=
87
get_command_type
(
tmp_buf
)
;
88
s =
menu
->
line
[
menu
->
line_idx
]
->
raw_text
;
89
l =
min
(strlen(s), (size_t)(
MAXLEN
- 1));
90
if
(l >
menu
->
choice_max_len
)
91
menu
->
choice_max_len
= l;
92
// if the choice text starts with '-' or '_',
93
// reserve the next character as the choice_letter
94
// and remove the choice_letter designator from the choice text
95
if
(*s ==
'-'
|| *s ==
'_'
) {
96
s++;
97
ltr = *s;
98
if
(ltr > 31 && ltr < 127) {
99
if
(!fltr[ltr]) {
100
fltr[ltr] = true;
101
menu
->
line
[
menu
->
line_idx
]
->
choice_letter
= *s;
102
}
103
}
104
s++;
105
}
106
strnz__cpy
(
tmp_buf
,
" x - "
,
MAXLEN
- 1
)
;
107
strnz__cat
(
tmp_buf
,
s
,
MAXLEN
- 1
)
;
108
menu
->
line
[
menu
->
line_idx
]
->
choice_text
= strdup(tmp_buf);
109
menu
->
line
[
menu
->
line_idx
]
->
type
=
MT_CHOICE
;
110
menu
->
line_idx
++;
111
commands++;
112
break
;
113
/** ':' Choice */
114
case
':'
:
115
if
(choices > commands) {
116
ssnprintf
(
em0
,
MAXLEN
- 1
,
117
"More choices than commands at line %d of"
,
118
in_fp_line
)
;
119
strnz__cpy
(
em1
,
menu
->
mapp_spec
,
MAXLEN
- 1
)
;
120
strnz__cpy
(
em2
,
in_buf
,
MAXLEN
- 1
)
;
121
display_error
(
em0
,
em1
,
em2
,
nullptr
)
;
122
abend
(
-1
,
"unrecoverable error"
)
;
123
}
124
l = strlen(tmp_buf);
125
menu
->
text_max_len
=
max
(
menu
->
text_max_len
, l);
126
if
(!
menu
->
title
[0]) {
/**< in_buf -> Title */
127
// if menu title is not set, use this line as the title,
128
l =
min
(l, (
MAXLEN
- 5));
129
strnz__cpy
(
menu
->
title
,
tmp_buf
,
l
)
;
130
l += 4;
131
menu
->
text_max_len
=
max
(
menu
->
text_max_len
, l);
132
}
else
{
133
// otherwise add it as a text line
134
menu
->
line
[
menu
->
line_idx
] = calloc(1,
sizeof
(Line));
135
if
(
menu
->
line
[
menu
->
line_idx
] == (Line *)0) {
136
sprintf(tmp_str,
137
"2-malloc(%ld bytes) failed menu->line[%d]"
,
138
sizeof
(Line),
menu
->
line_idx
);
139
abend
(
-1
,
tmp_str
)
;
140
}
141
menu
->
line
[
menu
->
line_idx
]
->
type
=
MT_TEXT
;
142
menu
->
line
[
menu
->
line_idx
]
->
raw_text
= strdup(tmp_buf);
143
menu
->
line_idx
++;
144
choices++;
145
}
146
break
;
147
case
'?'
:
148
break
;
/** ' ' Empty line, ignore */
149
case
' '
:
150
case
'\0'
:
151
case
'\n'
:
152
break
;
153
default
:
154
ssnprintf
(
em0
,
MAXLEN
- 1
,
"Invalid directive '%c' at line %d of"
,
155
directive
,
in_fp_line
)
;
156
strnz__cpy
(
em1
,
menu
->
mapp_spec
,
MAXLEN
- 1
)
;
157
strnz__cpy
(
em2
,
in_buf
,
MAXLEN
- 1
)
;
158
display_error
(
em0
,
em1
,
em2
,
nullptr
)
;
159
}
160
}
161
fclose(fp);
162
menu
->
item_count
=
menu
->
line_idx
;
163
for
(
menu
->
line_idx
= 0;
menu
->
line_idx
<
menu
->
item_count
;
164
menu
->
line_idx
++) {
165
menu
->
line
[
menu
->
line_idx
]
->
letter_pos
= 1;
166
// Try to get a choice_letter
167
// skip past " x - "
168
if
(
menu
->
line
[
menu
->
line_idx
]
->
choice_letter
!=
'\0'
) {
169
ltr =
menu
->
line
[
menu
->
line_idx
]
->
choice_letter
;
170
s =
menu
->
line
[
menu
->
line_idx
]
->
choice_text
+ 5;
171
while
(*s !=
'\0'
) {
172
if
(*s == ltr) {
173
menu
->
line
[
menu
->
line_idx
]
->
letter_pos
=
174
s -
menu
->
line
[
menu
->
line_idx
]
->
choice_text
;
175
break
;
176
}
177
s++;
178
}
179
fltr[ltr] = true;
180
}
else
{
181
s =
menu
->
line
[
menu
->
line_idx
]
->
choice_text
+ 5;
182
// Search string for first character that is not a space and not
183
// already used as a choice_letter
184
while
(*s !=
'\0'
) {
185
if
(*s !=
' '
)
186
if
(!fltr[(
int
)(uintptr_t)*s]) {
187
ltr = *s;
188
fltr[ltr] = true;
189
break
;
190
}
191
s++;
192
}
193
if
(*s !=
'\0'
) {
194
menu
->
line
[
menu
->
line_idx
]
->
letter_pos
=
195
s -
menu
->
line
[
menu
->
line_idx
]
->
choice_text
;
196
ltr = *s;
197
}
else
{
198
// If no letter found in choice text, find the first unused
199
// letter in the ASCII range 32-126
200
for
(ltr =
'0'
; ltr < 127; ltr++)
201
if
(fltr[ltr] == false) {
202
fltr[ltr] = true;
203
break
;
204
}
205
if
(ltr > 126) {
206
Perror
(
"Ran out of letters"
)
;
207
return
0;
208
}
209
}
210
}
211
menu
->
line
[
menu
->
line_idx
]
->
choice_letter
= ltr;
212
menu
->
line
[
menu
->
line_idx
]
->
choice_text
[1] = ltr;
213
}
214
menu
->
lines
=
menu
->
item_count
;
215
if
(
menu
->
text_max_len
> (
menu
->
choice_max_len
+ 6))
216
menu
->
cols
=
menu
->
text_max_len
;
217
else
218
menu
->
cols
=
menu
->
choice_max_len
+ 6;
219
if
(
menu
->
cols
>=
MAXLEN
)
220
Perror
(
"line too long"
)
;
221
return
0;
222
}
223
/** @brief Get command type from command string
224
@ingroup parse_menu
225
@param t Command string
226
@return Command type as an unsigned int
227
*/
228
unsigned
int
get_command_type
(
char
*t) {
229
char
*s, *p;
230
231
s = p = t;
232
while
(*s !=
' '
&& *s !=
'\0'
) {
233
if
(*s ==
'/'
)
234
p = s + 1;
235
s++;
236
}
237
*s =
'\0'
;
238
if
(!strcmp(p,
"ckeys"
))
239
return
(
CT_CKEYS
);
240
else
if
(!strcmp(p,
"dmon"
))
241
return
(
CT_DMON
);
242
else
if
(!strcmp(p,
"exec"
))
243
return
(
CT_EXEC
);
244
else
if
(!strcmp(p,
"help"
))
245
return
(
CT_HELP
);
246
else
if
(!strcmp(p,
"about"
))
247
return
(
CT_ABOUT
);
248
else
if
(!strcmp(p,
"menu"
))
249
return
(
CT_MENU
);
250
else
if
(!strcmp(p,
"form"
))
251
return
(
CT_FORM
);
252
else
if
(!strcmp(p,
"pick"
))
253
return
(
CT_PICK
);
254
else
if
(!strcmp(p,
"return"
))
255
return
(
CT_RETURN
);
256
else
if
(!strcmp(p,
"view"
))
257
return
(
CT_VIEW
);
258
else
if
(!strcmp(p,
"?"
))
259
return
(
CT_HELP
);
260
else
if
(!strcmp(p,
"write_config"
))
261
return
(
CT_WRITE_CONFIG
);
262
return
(
CT_UNDEFINED
);
263
}
nullptr
#define nullptr
Definition
cm.h:25
min
#define min(x, y)
min macro evaluates two expressions, returning least result
Definition
cm.h:56
max
#define max(a, b)
max macro evaluates two expressions, returning greatest result.
Definition
cm.h:49
menu
Menu * menu
Definition
mem.c:45
MT_TEXT
@ MT_TEXT
Definition
menu.h:23
MT_CHOICE
@ MT_CHOICE
Definition
menu.h:24
CT_PICK
@ CT_PICK
Definition
menu.h:46
CT_FORM
@ CT_FORM
Definition
menu.h:42
CT_UNDEFINED
@ CT_UNDEFINED
Definition
menu.h:52
CT_MENU
@ CT_MENU
Definition
menu.h:45
CT_RETURN
@ CT_RETURN
Definition
menu.h:49
CT_HELP
@ CT_HELP
Definition
menu.h:40
CT_WRITE_CONFIG
@ CT_WRITE_CONFIG
Definition
menu.h:51
CT_EXEC
@ CT_EXEC
Definition
menu.h:39
CT_VIEW
@ CT_VIEW
Definition
menu.h:47
CT_ABOUT
@ CT_ABOUT
Definition
menu.h:41
CT_DMON
@ CT_DMON
Definition
menu.h:38
CT_CKEYS
@ CT_CKEYS
Definition
menu.h:48
MAXLEN
#define MAXLEN
Definition
curskeys.c:15
em1
char em1[MAXLEN]
Definition
dwin.c:142
em0
char em0[MAXLEN]
Definition
dwin.c:141
em2
char em2[MAXLEN]
Definition
dwin.c:143
Perror
int Perror(char *)
Display a simple error message window or print to stderr.
Definition
dwin.c:1162
display_error
int display_error(char *em0, char *em1, char *em2, char *em3)
Display an error message window or print to stderr.
Definition
dwin.c:1102
abend
void abend(int, char *)
Abnormal program termination.
Definition
dwin.c:1588
strnz__cpy
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition
futil.c:435
trim
size_t trim(char *)
Trims leading and trailing spaces from string s in place.
Definition
futil.c:282
ssnprintf
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition
futil.c:311
strnz__cat
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition
futil.c:464
chrep
bool chrep(char *, char, char)
Replaces all occurrences of old_chr in s with new_chr in place.
Definition
futil.c:652
get_command_type
unsigned int get_command_type(char *)
Get command type from command string.
Definition
parse_menu_desc.c:228
parse_menu_description
unsigned int parse_menu_description(Init *)
Parse menu description file and create Menu.
Definition
parse_menu_desc.c:29
Init::menu
Menu * menu
Definition
common.h:169
Line::choice_text
char * choice_text
Definition
menu.h:63
Line::raw_text
char * raw_text
Definition
menu.h:60
Line::command_str
char * command_str
Definition
menu.h:79
Line::type
unsigned int type
Definition
menu.h:58
Line::letter_pos
int letter_pos
Definition
menu.h:69
Line::command_type
unsigned int command_type
Definition
menu.h:73
Line::choice_letter
char choice_letter
Definition
menu.h:66
Menu::cols
int cols
Definition
menu.h:101
Menu::mapp_spec
char mapp_spec[MAXLEN]
Definition
menu.h:124
Menu::line
Line * line[MAX_MENU_LINES]
Definition
menu.h:203
Menu::text_max_len
int text_max_len
Definition
menu.h:190
Menu::item_count
int item_count
Definition
menu.h:196
Menu::choice_max_len
int choice_max_len
Definition
menu.h:184
Menu::line_idx
int line_idx
Definition
menu.h:199
Menu::lines
int lines
Definition
menu.h:99
Menu::title
char title[MAXLEN]
Definition
menu.h:115
parse_menu_desc.c
Generated by
1.17.0