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 *menu = init
->
menu
;
44
for
(ltr = 0; ltr < 32; ltr++)
45
fltr[ltr] =
true
;
46
for
(ltr = 32; ltr < 127; ltr++)
47
fltr[ltr] =
false
;
48
fltr[
'q'
] =
true
;
49
fp = fopen(menu
->
mapp_spec
,
"r"
);
50
if
(fp ==
nullptr
) {
51
strnz__cpy
(
tmp_buf
,
"file not found"
,
MAXLEN
)
;
52
abend
(
-1
,
tmp_buf
)
;
53
exit(-1);
54
}
55
while
((fgets(in_buf,
MAXLEN
, fp)) !=
nullptr
) {
56
if
(in_buf[0] ==
'\0'
)
57
continue
;
58
in_fp_line++;
59
// Replace carriage returns, newlines, and tabs in in_buf with null
60
// terminators and spaces
61
chrep
(
in_buf
,
'\r'
,
'\0'
)
;
62
chrep
(
in_buf
,
'\n'
,
'\0'
)
;
63
chrep
(
in_buf
,
'\t'
,
' '
)
;
64
in_buf_p = in_buf;
65
directive = *in_buf_p;
66
in_buf_p++;
67
68
strnz__cpy
(
tmp_buf
,
in_buf_p
,
MAXLEN
)
;
69
trim
(
tmp_buf
)
;
70
l = strlen(tmp_buf);
71
if
(l == 0)
72
continue
;
73
if
(directive ==
'#'
)
74
continue
;
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
ltr = *s;
186
if
(ltr !=
' '
) {
187
if
(!fltr[ltr]) {
188
fltr[ltr] =
true
;
189
break
;
190
}
191
}
192
s++;
193
}
194
if
(*s !=
'\0'
) {
195
menu
->
line
[menu
->
line_idx
]
->
letter_pos
=
196
s - menu
->
line
[menu
->
line_idx
]
->
choice_text
;
197
ltr = *s;
198
}
else
{
199
// If no letter found in choice text, find the first unused
200
// letter in the ASCII range 32-126
201
for
(ltr =
'0'
; ltr < 127; ltr++)
202
if
(fltr[ltr] ==
false
) {
203
fltr[ltr] =
true
;
204
break
;
205
}
206
if
(ltr > 126) {
207
Perror
(
"Ran out of letters"
)
;
208
return
0;
209
}
210
}
211
}
212
menu
->
line
[menu
->
line_idx
]
->
choice_letter
= ltr;
213
menu
->
line
[menu
->
line_idx
]
->
choice_text
[1] = ltr;
214
}
215
menu
->
lines
= menu
->
item_count
;
216
if
(menu
->
text_max_len
> (menu
->
choice_max_len
+ 6))
217
menu
->
cols
= menu
->
text_max_len
;
218
else
219
menu
->
cols
= menu
->
choice_max_len
+ 6;
220
if
(menu
->
cols
>=
MAXLEN
)
221
Perror
(
"line too long"
)
;
222
return
0;
223
}
224
/** @brief Get command type from command string
225
@ingroup parse_menu
226
@param t Command string
227
@return Command type as an unsigned int
228
*/
229
unsigned
int
get_command_type
(
char
*t) {
230
char
*s, *p;
231
232
s = p = t;
233
while
(*s !=
' '
&& *s !=
'\0'
) {
234
if
(*s ==
'/'
)
235
p = s + 1;
236
s++;
237
}
238
*s =
'\0'
;
239
if
(!strcmp(p,
"ckeys"
))
240
return
(
CT_CKEYS
);
241
else
if
(!strcmp(p,
"exec"
))
242
return
(
CT_EXEC
);
243
else
if
(!strcmp(p,
"dexe"
))
244
return
(
CT_DEXE
);
245
else
if
(!strcmp(p,
"help"
))
246
return
(
CT_HELP
);
247
else
if
(!strcmp(p,
"about"
))
248
return
(
CT_ABOUT
);
249
else
if
(!strcmp(p,
"menu"
))
250
return
(
CT_MENU
);
251
else
if
(!strcmp(p,
"form"
))
252
return
(
CT_FORM
);
253
else
if
(!strcmp(p,
"pick"
))
254
return
(
CT_PICK
);
255
else
if
(!strcmp(p,
"return"
))
256
return
(
CT_RETURN
);
257
else
if
(!strcmp(p,
"view"
))
258
return
(
CT_VIEW
);
259
else
if
(!strcmp(p,
"?"
))
260
return
(
CT_HELP
);
261
else
if
(!strcmp(p,
"write_config"
))
262
return
(
CT_WRITE_CONFIG
);
263
return
(
CT_UNDEFINED
);
264
}
em1
char em1[MAXLEN]
Definition
dwin.c:176
min
#define min(x, y)
min macro evaluates two expressions, returning least result
Definition
cm.h:89
em0
char em0[MAXLEN]
Definition
dwin.c:175
em2
char em2[MAXLEN]
Definition
dwin.c:177
max
#define max(a, b)
max macro evaluates two expressions, returning greatest result.
Definition
cm.h:82
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_DEXE
@ CT_DEXE
Definition
menu.h:38
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_CKEYS
@ CT_CKEYS
Definition
menu.h:48
MAXLEN
#define MAXLEN
Definition
curskeys.c:15
Perror
int Perror(char *)
Display a simple error message window or print to stderr.
Definition
dwin.c:1526
abend
void abend(int, char *)
Abnormal program termination.
Definition
dwin.c:2018
display_error
int display_error(char *, char *, char *, char *)
Display an error message window or print to stderr.
Definition
dwin.c:1467
strnz__cpy
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition
futil.c:544
trim
size_t trim(char *)
Trims leading and trailing spaces from string s in place.
Definition
futil.c:391
ssnprintf
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition
futil.c:420
strnz__cat
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition
futil.c:573
chrep
bool chrep(char *, char, char)
Replaces all occurrences of old_chr in s with new_chr in place.
Definition
futil.c:750
get_command_type
unsigned int get_command_type(char *)
Get command type from command string.
Definition
parse_menu_desc.c:229
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:182
Line::choice_text
char * choice_text
Definition
menu.h:79
Line::raw_text
char * raw_text
Definition
menu.h:76
Line::command_str
char * command_str
Definition
menu.h:95
Line::type
unsigned int type
Definition
menu.h:74
Line::letter_pos
int letter_pos
Definition
menu.h:85
Line::command_type
unsigned int command_type
Definition
menu.h:89
Line::choice_letter
char choice_letter
Definition
menu.h:82
Menu::cols
int cols
Definition
menu.h:117
Menu::mapp_spec
char mapp_spec[MAXLEN]
Definition
menu.h:140
Menu::line
Line * line[MAX_MENU_LINES]
Definition
menu.h:219
Menu::text_max_len
int text_max_len
Definition
menu.h:206
Menu::item_count
int item_count
Definition
menu.h:212
Menu::choice_max_len
int choice_max_len
Definition
menu.h:200
Menu::line_idx
int line_idx
Definition
menu.h:215
Menu::lines
int lines
Definition
menu.h:115
Menu::title
char title[MAXLEN]
Definition
menu.h:131
parse_menu_desc.c
Generated by
1.17.0