Download
jcs
/subtext
/main_menu.c
(View History)
jcs board+main_menu: Add ACTION_BOARD_LIST_BOARDS, fix bug in indexing | Latest amendment: 464 on 2023-04-06 |
1 | /* |
2 | * Copyright (c) 2022 joshua stein <jcs@jcs.org> |
3 | * |
4 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #include <stdio.h> |
18 | #include <string.h> |
19 | |
20 | #include "subtext.h" |
21 | #include "db.h" |
22 | #include "main_menu.h" |
23 | |
24 | struct main_menu_option *main_menu_options = NULL; |
25 | |
26 | void |
27 | main_menu_init(void) |
28 | { |
29 | struct main_menu_option *opts = NULL, *old_opts = NULL; |
30 | size_t size; |
31 | char *menu = NULL; |
32 | Handle default_menu; |
33 | |
34 | size = bile_read_alloc(db->bile, DB_TEXT_TYPE, DB_TEXT_MENU_OPTIONS_ID, |
35 | &menu); |
36 | if (size == 0 || menu == NULL) { |
37 | /* use default menu */ |
38 | if (menu != NULL) |
39 | xfree(&menu); |
40 | default_menu = GetResource('TEXT', MENU_DEFAULTS_ID); |
41 | if (default_menu == NULL) |
42 | panic("Failed to find TEXT resource %d", MENU_DEFAULTS_ID); |
43 | size = GetHandleSize(default_menu); |
44 | menu = xmalloc(size); |
45 | if (menu == NULL) |
46 | panic("No memory for main menu"); |
47 | HLock(default_menu); |
48 | memcpy(menu, *default_menu, size); |
49 | HUnlock(default_menu); |
50 | ReleaseResource(default_menu); |
51 | |
52 | bile_write(db->bile, DB_TEXT_TYPE, DB_TEXT_MENU_OPTIONS_ID, |
53 | menu, size); |
54 | } |
55 | |
56 | opts = main_menu_parse(menu, size); |
57 | xfree(&menu); |
58 | if (opts == NULL) { |
59 | main_menu_edit(); |
60 | return; |
61 | } |
62 | |
63 | /* swap this atomically */ |
64 | old_opts = main_menu_options; |
65 | main_menu_options = opts; |
66 | if (old_opts != NULL) |
67 | xfree(&old_opts); |
68 | } |
69 | |
70 | void |
71 | main_menu_edit(void) |
72 | { |
73 | view_editor_show(DB_TEXT_MENU_OPTIONS_ID, "Edit: Main Menu options"); |
74 | } |
75 | |
76 | struct main_menu_option * |
77 | main_menu_parse(char *opts, size_t len) |
78 | { |
79 | char *line, *action, *menu_key, *all_keys, *label; |
80 | size_t n, m, linelen, lastsep, linenum, ret_size; |
81 | short actionid, ret_count; |
82 | struct main_menu_option *ret = NULL; |
83 | |
84 | ret_count = 0; |
85 | ret_size = 0; |
86 | |
87 | #define LINESIZE 1024 |
88 | line = xmalloc(LINESIZE); |
89 | if (line == NULL) |
90 | goto parse_done; |
91 | action = xmalloc(LINESIZE); |
92 | if (action == NULL) |
93 | goto parse_done; |
94 | menu_key = xmalloc(LINESIZE); |
95 | if (menu_key == NULL) |
96 | goto parse_done; |
97 | all_keys = xmalloc(LINESIZE); |
98 | if (all_keys == NULL) |
99 | goto parse_done; |
100 | label = xmalloc(LINESIZE); |
101 | if (label == NULL) |
102 | goto parse_done; |
103 | |
104 | for (n = 0, lastsep = 0, linenum = 0; n <= len; n++) { |
105 | if (!(n == len || opts[n] == '\r')) |
106 | continue; |
107 | |
108 | linenum++; |
109 | linelen = MIN(n - lastsep, LINESIZE - 1); |
110 | if (n == len && linelen) |
111 | linelen--; |
112 | memcpy(line, opts + lastsep, linelen); |
113 | line[linelen] = '\0'; |
114 | lastsep = n + 1; |
115 | |
116 | if (line[0] == '\0' || line[0] == '#' || line[0] == '\r') |
117 | continue; |
118 | |
119 | /* BOARD_SHOW_FIRST:B:Bb:Message Board */ |
120 | |
121 | /* sscanf won't match %[^:] for an empty section ("A::Aa:aa") */ |
122 | action[0] = '\0'; |
123 | for (m = 0; m < linelen; m++) { |
124 | if (line[m] == ':') { |
125 | memcpy(action, line, m); |
126 | action[m] = '\0'; |
127 | linelen -= m + 1; |
128 | memmove(line, line + m + 1, linelen + 1); |
129 | break; |
130 | } |
131 | } |
132 | if (action[0] == '\0') { |
133 | warn("Error on line %ld: no action found", linenum); |
134 | ret_count = 0; |
135 | break; |
136 | } |
137 | |
138 | actionid = ACTION_NONE; |
139 | if (strcmp(action, "BOARD_LIST_BOARDS") == 0) |
140 | actionid = ACTION_BOARD_LIST_BOARDS; |
141 | else if (strcmp(action, "BOARD_LIST_FTN_AREAS") == 0) |
142 | actionid = ACTION_BOARD_LIST_FTN_AREAS; |
143 | else if (strcmp(action, "BOARD_SHOW_FIRST") == 0) |
144 | actionid = ACTION_BOARD_SHOW_FIRST; |
145 | else if (strcmp(action, "BOARD_SHOW_1") == 0) |
146 | actionid = ACTION_BOARD_SHOW_1; |
147 | else if (strcmp(action, "BOARD_SHOW_2") == 0) |
148 | actionid = ACTION_BOARD_SHOW_2; |
149 | else if (strcmp(action, "BOARD_SHOW_3") == 0) |
150 | actionid = ACTION_BOARD_SHOW_3; |
151 | else if (strcmp(action, "BOARD_SHOW_4") == 0) |
152 | actionid = ACTION_BOARD_SHOW_4; |
153 | else if (strcmp(action, "BOARD_SHOW_5") == 0) |
154 | actionid = ACTION_BOARD_SHOW_5; |
155 | else if (strcmp(action, "BOARD_SHOW_6") == 0) |
156 | actionid = ACTION_BOARD_SHOW_6; |
157 | else if (strcmp(action, "BOARD_SHOW_7") == 0) |
158 | actionid = ACTION_BOARD_SHOW_7; |
159 | else if (strcmp(action, "BOARD_SHOW_8") == 0) |
160 | actionid = ACTION_BOARD_SHOW_8; |
161 | else if (strcmp(action, "BOARD_SHOW_9") == 0) |
162 | actionid = ACTION_BOARD_SHOW_9; |
163 | else if (strcmp(action, "BOARD_SHOW_10") == 0) |
164 | actionid = ACTION_BOARD_SHOW_10; |
165 | else if (strcmp(action, "CHAT") == 0) |
166 | actionid = ACTION_CHAT; |
167 | else if (strcmp(action, "FILES_MENU") == 0) |
168 | actionid = ACTION_FILES_MENU; |
169 | else if (strcmp(action, "GOODBYE") == 0) |
170 | actionid = ACTION_GOODBYE; |
171 | else if (strcmp(action, "RECENT_LOGINS") == 0) |
172 | actionid = ACTION_RECENT_LOGINS; |
173 | else if (strcmp(action, "MAIL_COMPOSE") == 0) |
174 | actionid = ACTION_MAIL_COMPOSE; |
175 | else if (strcmp(action, "MAIL_MENU") == 0) |
176 | actionid = ACTION_MAIL_MENU; |
177 | else if (strcmp(action, "MOTD") == 0) |
178 | actionid = ACTION_MOTD; |
179 | else if (strcmp(action, "PAGE_SEND_OR_ANSWER") == 0) |
180 | actionid = ACTION_PAGE_SEND_OR_ANSWER; |
181 | else if (strcmp(action, "SETTINGS_OR_SIGNUP") == 0) |
182 | actionid = ACTION_SETTINGS_OR_SIGNUP; |
183 | else if (strcmp(action, "SHOW_MENU") == 0) |
184 | actionid = ACTION_SHOW_MENU; |
185 | else if (strcmp(action, "SYSOP_MENU") == 0) |
186 | actionid = ACTION_SYSOP_MENU; |
187 | else if (strcmp(action, "WHOS_ONLINE") == 0) |
188 | actionid = ACTION_WHOS_ONLINE; |
189 | else { |
190 | warn("Error on line %ld: invalid action \"%s\"", linenum, |
191 | action); |
192 | ret_count = 0; |
193 | break; |
194 | } |
195 | |
196 | menu_key[0] = '\0'; |
197 | for (m = 0; m < linelen; m++) { |
198 | if (line[m] == ':') { |
199 | memcpy(menu_key, line, m); |
200 | menu_key[m] = '\0'; |
201 | linelen -= m + 1; |
202 | memmove(line, line + m + 1, linelen + 1); |
203 | break; |
204 | } |
205 | } |
206 | /* menu_key can be 1 or 0 characters */ |
207 | if (strlen(menu_key) > 1) { |
208 | warn("Error on line %ld: Menu Key can only be 1 character", |
209 | linenum); |
210 | ret_count = 0; |
211 | break; |
212 | } |
213 | |
214 | all_keys[0] = '\0'; |
215 | for (m = 0; m < linelen; m++) { |
216 | if (line[m] == ':') { |
217 | memcpy(all_keys, line, m); |
218 | all_keys[m] = '\0'; |
219 | linelen -= m + 1; |
220 | memmove(line, line + m + 1, linelen + 1); |
221 | break; |
222 | } |
223 | } |
224 | if (all_keys[0] == '\0') { |
225 | warn("Error on line %ld: All Keys field cannot be empty", |
226 | linenum); |
227 | ret_count = 0; |
228 | break; |
229 | } |
230 | |
231 | if (line[0] == '\0') { |
232 | warn("Error on line %ld: Label cannot be empty", |
233 | linenum); |
234 | ret_count = 0; |
235 | break; |
236 | } |
237 | strlcpy(label, line, LINESIZE); |
238 | |
239 | if (!grow_to_fit(&ret, &ret_size, |
240 | sizeof(struct main_menu_option) * ret_count, |
241 | sizeof(struct main_menu_option), 4)) { |
242 | ret_count = 0; |
243 | break; |
244 | } |
245 | |
246 | ret[ret_count].action = actionid; |
247 | ret[ret_count].menu_key = menu_key[0]; |
248 | strlcpy(ret[ret_count].all_keys, all_keys, |
249 | sizeof(ret[ret_count].all_keys)); |
250 | strlcpy(ret[ret_count].label, label, sizeof(ret[ret_count].label)); |
251 | ret_count++; |
252 | } |
253 | |
254 | parse_done: |
255 | if (line) |
256 | xfree(&line); |
257 | if (action) |
258 | xfree(&action); |
259 | if (menu_key) |
260 | xfree(&menu_key); |
261 | if (all_keys) |
262 | xfree(&all_keys); |
263 | if (label) |
264 | xfree(&label); |
265 | |
266 | if (ret_count == 0) { |
267 | if (ret) |
268 | xfree(&ret); |
269 | return NULL; |
270 | } |
271 | |
272 | if (!grow_to_fit(&ret, &ret_size, |
273 | sizeof(struct main_menu_option) * ret_count, |
274 | sizeof(struct main_menu_option), 1)) { |
275 | if (ret) |
276 | xfree(&ret); |
277 | return NULL; |
278 | } |
279 | |
280 | ret[ret_count].action = ACTION_NONE; |
281 | ret[ret_count].menu_key = 0; |
282 | ret[ret_count].all_keys[0] = '\0'; |
283 | ret[ret_count].label[0] = '\0'; |
284 | |
285 | return ret; |
286 | #undef LINESIZE |
287 | } |