| 1 | /* | 
|---|
| 2 | ** Copyright (C) 1991, 1997 Free Software Foundation, Inc. | 
|---|
| 3 | ** | 
|---|
| 4 | ** This file is part of TACK. | 
|---|
| 5 | ** | 
|---|
| 6 | ** TACK is free software; you can redistribute it and/or modify | 
|---|
| 7 | ** it under the terms of the GNU General Public License as published by | 
|---|
| 8 | ** the Free Software Foundation; either version 2, or (at your option) | 
|---|
| 9 | ** any later version. | 
|---|
| 10 | ** | 
|---|
| 11 | ** TACK is distributed in the hope that it will be useful, | 
|---|
| 12 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 14 | ** GNU General Public License for more details. | 
|---|
| 15 | ** | 
|---|
| 16 | ** You should have received a copy of the GNU General Public License | 
|---|
| 17 | ** along with TACK; see the file COPYING.  If not, write to | 
|---|
| 18 | ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 
|---|
| 19 | ** Boston, MA 02110-1301, USA | 
|---|
| 20 | */ | 
|---|
| 21 |  | 
|---|
| 22 | #include <tack.h> | 
|---|
| 23 |  | 
|---|
| 24 | MODULE_ID("$Id: menu.c,v 1.3 2005/09/17 19:49:16 tom Exp $") | 
|---|
| 25 |  | 
|---|
| 26 | /* | 
|---|
| 27 | Menu control | 
|---|
| 28 | */ | 
|---|
| 29 |  | 
|---|
| 30 | static void test_byname(struct test_menu *, int *, int *); | 
|---|
| 31 |  | 
|---|
| 32 | struct test_list *augment_test; | 
|---|
| 33 | char prompt_string[80]; /* menu prompt storage */ | 
|---|
| 34 |  | 
|---|
| 35 | /* | 
|---|
| 36 | **      menu_prompt() | 
|---|
| 37 | ** | 
|---|
| 38 | **      Print the menu prompt string. | 
|---|
| 39 | */ | 
|---|
| 40 | void | 
|---|
| 41 | menu_prompt(void) | 
|---|
| 42 | { | 
|---|
| 43 | ptext(&prompt_string[1]); | 
|---|
| 44 | } | 
|---|
| 45 |  | 
|---|
| 46 | /* | 
|---|
| 47 | **      menu_test_loop(test-structure, state, control-character) | 
|---|
| 48 | ** | 
|---|
| 49 | **      This function implements the repeat test function. | 
|---|
| 50 | */ | 
|---|
| 51 | static void | 
|---|
| 52 | menu_test_loop( | 
|---|
| 53 | struct test_list *test, | 
|---|
| 54 | int *state, | 
|---|
| 55 | int *ch) | 
|---|
| 56 | { | 
|---|
| 57 | int nch, p; | 
|---|
| 58 |  | 
|---|
| 59 | if ((test->flags & MENU_REP_MASK) && (augment_test != test)) { | 
|---|
| 60 | /* set the augment variable (first time only) */ | 
|---|
| 61 | p = (test->flags >> 8) & 15; | 
|---|
| 62 | if ((test->flags & MENU_REP_MASK) == MENU_LM1) { | 
|---|
| 63 | augment = lines - 1; | 
|---|
| 64 | } else | 
|---|
| 65 | if ((test->flags & MENU_ONE_MASK) == MENU_ONE) { | 
|---|
| 66 | augment = 1; | 
|---|
| 67 | } else | 
|---|
| 68 | if ((test->flags & MENU_LC_MASK) == MENU_lines) { | 
|---|
| 69 | augment = lines * p / 10; | 
|---|
| 70 | } else | 
|---|
| 71 | if ((test->flags & MENU_LC_MASK) == MENU_columns) { | 
|---|
| 72 | augment = columns * p / 10; | 
|---|
| 73 | } else { | 
|---|
| 74 | augment = 1; | 
|---|
| 75 | } | 
|---|
| 76 | augment_test = test; | 
|---|
| 77 | set_augment_txt(); | 
|---|
| 78 | } | 
|---|
| 79 | do { | 
|---|
| 80 | if ((test->flags | *state) & MENU_CLEAR) { | 
|---|
| 81 | put_clear(); | 
|---|
| 82 | } else | 
|---|
| 83 | if (line_count + test->lines_needed >= lines) { | 
|---|
| 84 | put_clear(); | 
|---|
| 85 | } | 
|---|
| 86 | nch = 0; | 
|---|
| 87 | if (test->test_procedure) { | 
|---|
| 88 | /* The procedure takes precedence so I can pass | 
|---|
| 89 | the menu entry as an argument. | 
|---|
| 90 | */ | 
|---|
| 91 | can_test(test->caps_done, FLAG_TESTED); | 
|---|
| 92 | can_test(test->caps_tested, FLAG_TESTED); | 
|---|
| 93 | test->test_procedure(test, state, &nch); | 
|---|
| 94 | } else | 
|---|
| 95 | if (test->sub_menu) { | 
|---|
| 96 | /* nested menu's */ | 
|---|
| 97 | menu_display(test->sub_menu, &nch); | 
|---|
| 98 | *state = 0; | 
|---|
| 99 | if (nch == 'q' || nch == 's') { | 
|---|
| 100 | /* Quit and skip are killed here */ | 
|---|
| 101 | nch = '?'; | 
|---|
| 102 | } | 
|---|
| 103 | } else { | 
|---|
| 104 | break;  /* cya */ | 
|---|
| 105 | } | 
|---|
| 106 | if (nch == '\r' || nch == '\n' || nch == 'n') { | 
|---|
| 107 | nch = 0; | 
|---|
| 108 | break; | 
|---|
| 109 | } | 
|---|
| 110 | } while (nch == 'r'); | 
|---|
| 111 | *ch = nch; | 
|---|
| 112 | } | 
|---|
| 113 |  | 
|---|
| 114 | /* | 
|---|
| 115 | **      menu_display(menu-structure, flags) | 
|---|
| 116 | ** | 
|---|
| 117 | **      This function implements menu control. | 
|---|
| 118 | */ | 
|---|
| 119 | void | 
|---|
| 120 | menu_display( | 
|---|
| 121 | struct test_menu *menu, | 
|---|
| 122 | int *last_ch) | 
|---|
| 123 | { | 
|---|
| 124 | int test_state = 0, run_standard_tests; | 
|---|
| 125 | int hot_topic, ch = 0, nch = 0; | 
|---|
| 126 | struct test_list *mt; | 
|---|
| 127 | struct test_list *repeat_tests = 0; | 
|---|
| 128 | int repeat_state = 0; | 
|---|
| 129 | int prompt_length; | 
|---|
| 130 |  | 
|---|
| 131 | prompt_length = strlen(prompt_string); | 
|---|
| 132 | if (menu->ident) { | 
|---|
| 133 | sprintf(&prompt_string[prompt_length], "/%s", menu->ident); | 
|---|
| 134 | } | 
|---|
| 135 | hot_topic = menu->default_action; | 
|---|
| 136 | run_standard_tests = menu->standard_tests ? | 
|---|
| 137 | menu->standard_tests[0] : -1; | 
|---|
| 138 | if (!last_ch) { | 
|---|
| 139 | last_ch = &ch; | 
|---|
| 140 | } | 
|---|
| 141 | while (1) { | 
|---|
| 142 | if (ch == 0) { | 
|---|
| 143 | /* Display the menu */ | 
|---|
| 144 | put_crlf(); | 
|---|
| 145 | if (menu->menu_function) { | 
|---|
| 146 | /* | 
|---|
| 147 | this function may be used to restrict menu | 
|---|
| 148 | entries.  If used it must print the title. | 
|---|
| 149 | */ | 
|---|
| 150 | menu->menu_function(menu); | 
|---|
| 151 | } else | 
|---|
| 152 | if (menu->menu_title) { | 
|---|
| 153 | ptextln(menu->menu_title); | 
|---|
| 154 | } | 
|---|
| 155 | for (mt = menu->tests; (mt->flags & MENU_LAST) == 0; mt++) { | 
|---|
| 156 | if (mt->menu_entry) { | 
|---|
| 157 | ptext(" "); | 
|---|
| 158 | ptextln(mt->menu_entry); | 
|---|
| 159 | } | 
|---|
| 160 | } | 
|---|
| 161 | if (menu->standard_tests) { | 
|---|
| 162 | ptext(" "); | 
|---|
| 163 | ptextln(menu->standard_tests); | 
|---|
| 164 | ptextln(" r) repeat test"); | 
|---|
| 165 | ptextln(" s) skip to next test"); | 
|---|
| 166 | } | 
|---|
| 167 | ptextln(" q) quit"); | 
|---|
| 168 | ptextln(" ?) help"); | 
|---|
| 169 | } | 
|---|
| 170 | if (ch == 0 || ch == REQUEST_PROMPT) { | 
|---|
| 171 | put_crlf(); | 
|---|
| 172 | ptext(&prompt_string[1]); | 
|---|
| 173 | if (hot_topic) { | 
|---|
| 174 | ptext(" ["); | 
|---|
| 175 | putchp(hot_topic); | 
|---|
| 176 | ptext("]"); | 
|---|
| 177 | } | 
|---|
| 178 | ptext(" > "); | 
|---|
| 179 | /* read a character */ | 
|---|
| 180 | ch = wait_here(); | 
|---|
| 181 | } | 
|---|
| 182 | if (ch == '\r' || ch == '\n') { | 
|---|
| 183 | ch = hot_topic; | 
|---|
| 184 | } | 
|---|
| 185 | if (ch == 'q') { | 
|---|
| 186 | break; | 
|---|
| 187 | } | 
|---|
| 188 | if (ch == '?') { | 
|---|
| 189 | ch = 0; | 
|---|
| 190 | continue; | 
|---|
| 191 | } | 
|---|
| 192 | nch = ch; | 
|---|
| 193 | ch = 0; | 
|---|
| 194 | /* Run one of the standard tests (by request) */ | 
|---|
| 195 | for (mt = menu->tests; (mt->flags & MENU_LAST) == 0; mt++) { | 
|---|
| 196 | if (mt->menu_entry && (nch == mt->menu_entry[0])) { | 
|---|
| 197 | if (mt->flags & MENU_MENU) { | 
|---|
| 198 | test_byname(menu, &test_state, &nch); | 
|---|
| 199 | } else { | 
|---|
| 200 | menu_test_loop(mt, &test_state, &nch); | 
|---|
| 201 | } | 
|---|
| 202 | ch = nch; | 
|---|
| 203 | if ((mt->flags & MENU_COMPLETE) && ch == 0) { | 
|---|
| 204 | /* top level */ | 
|---|
| 205 | hot_topic = 'q'; | 
|---|
| 206 | ch = '?'; | 
|---|
| 207 | } | 
|---|
| 208 | } | 
|---|
| 209 | } | 
|---|
| 210 | if (menu->standard_tests && nch == 'r') { | 
|---|
| 211 | menu->resume_tests = repeat_tests; | 
|---|
| 212 | test_state = repeat_state; | 
|---|
| 213 | nch = run_standard_tests; | 
|---|
| 214 | } | 
|---|
| 215 | if (nch == run_standard_tests) { | 
|---|
| 216 | if (!(mt = menu->resume_tests)) { | 
|---|
| 217 | mt = menu->tests; | 
|---|
| 218 | } | 
|---|
| 219 | if (mt->flags & MENU_LAST) { | 
|---|
| 220 | mt = menu->tests; | 
|---|
| 221 | } | 
|---|
| 222 | /* Run the standard test suite */ | 
|---|
| 223 | for ( ; (mt->flags & MENU_LAST) == 0; ) { | 
|---|
| 224 | if ((mt->flags & MENU_NEXT) == MENU_NEXT) { | 
|---|
| 225 | repeat_tests = mt; | 
|---|
| 226 | repeat_state = test_state; | 
|---|
| 227 | nch = run_standard_tests; | 
|---|
| 228 | menu_test_loop(mt, &test_state, &nch); | 
|---|
| 229 | if (nch != 0 && nch != 'n') { | 
|---|
| 230 | ch = nch; | 
|---|
| 231 | break; | 
|---|
| 232 | } | 
|---|
| 233 | if (test_state & MENU_STOP) { | 
|---|
| 234 | break; | 
|---|
| 235 | } | 
|---|
| 236 | } | 
|---|
| 237 | mt++; | 
|---|
| 238 | } | 
|---|
| 239 | if (ch == 0) { | 
|---|
| 240 | ch = hot_topic; | 
|---|
| 241 | } | 
|---|
| 242 | menu->resume_tests = mt; | 
|---|
| 243 | menu->resume_state = test_state; | 
|---|
| 244 | menu->resume_char = ch; | 
|---|
| 245 |  | 
|---|
| 246 | if (ch == run_standard_tests) { | 
|---|
| 247 | /* pop up a level */ | 
|---|
| 248 | break; | 
|---|
| 249 | } | 
|---|
| 250 | } | 
|---|
| 251 | } | 
|---|
| 252 | *last_ch = ch; | 
|---|
| 253 | prompt_string[prompt_length] = '\0'; | 
|---|
| 254 | } | 
|---|
| 255 |  | 
|---|
| 256 | /* | 
|---|
| 257 | **      generic_done_message(test_list) | 
|---|
| 258 | ** | 
|---|
| 259 | **      Print the Done message and request input. | 
|---|
| 260 | */ | 
|---|
| 261 | void | 
|---|
| 262 | generic_done_message( | 
|---|
| 263 | struct test_list *test, | 
|---|
| 264 | int *state, | 
|---|
| 265 | int *ch) | 
|---|
| 266 | { | 
|---|
| 267 | char done_message[128]; | 
|---|
| 268 |  | 
|---|
| 269 | if (test->caps_done) { | 
|---|
| 270 | sprintf(done_message, "(%s) Done ", test->caps_done); | 
|---|
| 271 | ptext(done_message); | 
|---|
| 272 | } else { | 
|---|
| 273 | ptext("Done "); | 
|---|
| 274 | } | 
|---|
| 275 | *ch = wait_here(); | 
|---|
| 276 | if (*ch == '\r' || *ch == '\n' || *ch == 'n') { | 
|---|
| 277 | *ch = 0; | 
|---|
| 278 | } | 
|---|
| 279 | if (*ch == 's') { | 
|---|
| 280 | *state |= MENU_STOP; | 
|---|
| 281 | *ch = 0; | 
|---|
| 282 | } | 
|---|
| 283 | } | 
|---|
| 284 |  | 
|---|
| 285 | /* | 
|---|
| 286 | **      menu_clear_screen(test, state, ch) | 
|---|
| 287 | ** | 
|---|
| 288 | **      Just clear the screen. | 
|---|
| 289 | */ | 
|---|
| 290 | void | 
|---|
| 291 | menu_clear_screen( | 
|---|
| 292 | struct test_list *test GCC_UNUSED, | 
|---|
| 293 | int *state GCC_UNUSED, | 
|---|
| 294 | int *ch GCC_UNUSED) | 
|---|
| 295 | { | 
|---|
| 296 | put_clear(); | 
|---|
| 297 | } | 
|---|
| 298 |  | 
|---|
| 299 | /* | 
|---|
| 300 | **      menu_reset_init(test, state, ch) | 
|---|
| 301 | ** | 
|---|
| 302 | **      Send the reset and init strings. | 
|---|
| 303 | */ | 
|---|
| 304 | void | 
|---|
| 305 | menu_reset_init( | 
|---|
| 306 | struct test_list *test GCC_UNUSED, | 
|---|
| 307 | int *state GCC_UNUSED, | 
|---|
| 308 | int *ch GCC_UNUSED) | 
|---|
| 309 | { | 
|---|
| 310 | reset_init(); | 
|---|
| 311 | put_crlf(); | 
|---|
| 312 | } | 
|---|
| 313 |  | 
|---|
| 314 | /* | 
|---|
| 315 | **      subtest_menu(test, state, ch) | 
|---|
| 316 | ** | 
|---|
| 317 | **      Scan the menu looking for something to execute | 
|---|
| 318 | **      Return TRUE if we found anything. | 
|---|
| 319 | */ | 
|---|
| 320 | int | 
|---|
| 321 | subtest_menu( | 
|---|
| 322 | struct test_list *test, | 
|---|
| 323 | int *state, | 
|---|
| 324 | int *ch) | 
|---|
| 325 | { | 
|---|
| 326 | struct test_list *mt; | 
|---|
| 327 |  | 
|---|
| 328 | if (*ch) { | 
|---|
| 329 | for (mt = test; (mt->flags & MENU_LAST) == 0; mt++) { | 
|---|
| 330 | if (mt->menu_entry && (*ch == mt->menu_entry[0])) { | 
|---|
| 331 | *ch = 0; | 
|---|
| 332 | menu_test_loop(mt, state, ch); | 
|---|
| 333 | return TRUE; | 
|---|
| 334 | } | 
|---|
| 335 | } | 
|---|
| 336 | } | 
|---|
| 337 | return FALSE; | 
|---|
| 338 | } | 
|---|
| 339 |  | 
|---|
| 340 | /* | 
|---|
| 341 | **      menu_can_scan(menu-structure) | 
|---|
| 342 | ** | 
|---|
| 343 | **      Recursively scan the menu tree and find which cap names can be tested. | 
|---|
| 344 | */ | 
|---|
| 345 | void | 
|---|
| 346 | menu_can_scan( | 
|---|
| 347 | const struct test_menu *menu) | 
|---|
| 348 | { | 
|---|
| 349 | struct test_list *mt; | 
|---|
| 350 |  | 
|---|
| 351 | for (mt = menu->tests; (mt->flags & MENU_LAST) == 0; mt++) { | 
|---|
| 352 | can_test(mt->caps_done, FLAG_CAN_TEST); | 
|---|
| 353 | can_test(mt->caps_tested, FLAG_CAN_TEST); | 
|---|
| 354 | if (!(mt->test_procedure)) { | 
|---|
| 355 | if (mt->sub_menu) { | 
|---|
| 356 | menu_can_scan(mt->sub_menu); | 
|---|
| 357 | } | 
|---|
| 358 | } | 
|---|
| 359 | } | 
|---|
| 360 | } | 
|---|
| 361 |  | 
|---|
| 362 | /* | 
|---|
| 363 | **      menu_search(menu-structure, cap) | 
|---|
| 364 | ** | 
|---|
| 365 | **      Recursively search the menu tree and execute any tests that use cap. | 
|---|
| 366 | */ | 
|---|
| 367 | static void | 
|---|
| 368 | menu_search( | 
|---|
| 369 | struct test_menu *menu, | 
|---|
| 370 | int *state, | 
|---|
| 371 | int *ch, | 
|---|
| 372 | char *cap) | 
|---|
| 373 | { | 
|---|
| 374 | struct test_list *mt; | 
|---|
| 375 | int nch; | 
|---|
| 376 |  | 
|---|
| 377 | for (mt = menu->tests; (mt->flags & MENU_LAST) == 0; mt++) { | 
|---|
| 378 | nch = 0; | 
|---|
| 379 | if (cap_match(mt->caps_done, cap) | 
|---|
| 380 | || cap_match(mt->caps_tested, cap)) { | 
|---|
| 381 | menu_test_loop(mt, state, &nch); | 
|---|
| 382 | } | 
|---|
| 383 | if (!(mt->test_procedure)) { | 
|---|
| 384 | if (mt->sub_menu) { | 
|---|
| 385 | menu_search(mt->sub_menu, state, &nch, cap); | 
|---|
| 386 | } | 
|---|
| 387 | } | 
|---|
| 388 | if (*state & MENU_STOP) { | 
|---|
| 389 | break; | 
|---|
| 390 | } | 
|---|
| 391 | if (nch != 0 && nch != 'n') { | 
|---|
| 392 | *ch = nch; | 
|---|
| 393 | break; | 
|---|
| 394 | } | 
|---|
| 395 | } | 
|---|
| 396 | } | 
|---|
| 397 |  | 
|---|
| 398 | /* | 
|---|
| 399 | **      test_byname(menu, state, ch) | 
|---|
| 400 | ** | 
|---|
| 401 | **      Get a cap name then run all tests that use that cap. | 
|---|
| 402 | */ | 
|---|
| 403 | static void | 
|---|
| 404 | test_byname( | 
|---|
| 405 | struct test_menu *menu, | 
|---|
| 406 | int *state GCC_UNUSED, | 
|---|
| 407 | int *ch) | 
|---|
| 408 | { | 
|---|
| 409 | int test_state = 0; | 
|---|
| 410 | char cap[32]; | 
|---|
| 411 |  | 
|---|
| 412 | if (tty_can_sync == SYNC_NOT_TESTED) { | 
|---|
| 413 | verify_time(); | 
|---|
| 414 | } | 
|---|
| 415 | ptext("enter name: "); | 
|---|
| 416 | read_string(cap, sizeof(cap)); | 
|---|
| 417 | if (cap[0]) { | 
|---|
| 418 | menu_search(menu, &test_state, ch, cap); | 
|---|
| 419 | } | 
|---|
| 420 | *ch = '?'; | 
|---|
| 421 | } | 
|---|