| | 1 | /* $Header$ */ |
| | 2 | |
| | 3 | /* |
| | 4 | * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. |
| | 5 | * |
| | 6 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 7 | * on using and copying this software. |
| | 8 | */ |
| | 9 | /* |
| | 10 | Name |
| | 11 | os_stdio.cpp - OS implementation for standard I/O |
| | 12 | Function |
| | 13 | This is a simple stdio-based implementation of the OS display functions. |
| | 14 | This is a portable implementation (at least, it's portable to any |
| | 15 | platform where stdio can be used). |
| | 16 | |
| | 17 | The main purpose of this implementation is to make it easy to build |
| | 18 | command-line versions of tools that incorporate osifc-layer functions |
| | 19 | without having to drag in the full-blown OS-specific implementation. |
| | 20 | For example, test tools might find this useful. |
| | 21 | Notes |
| | 22 | |
| | 23 | Modified |
| | 24 | 09/04/99 MJRoberts - Creation |
| | 25 | */ |
| | 26 | |
| | 27 | #include <stdio.h> |
| | 28 | #include <ctype.h> |
| | 29 | #include <string.h> |
| | 30 | |
| | 31 | #include "os.h" |
| | 32 | #include "t3std.h" |
| | 33 | |
| | 34 | /* |
| | 35 | * status-line display mode |
| | 36 | */ |
| | 37 | static int S_status_mode = 0; |
| | 38 | |
| | 39 | /* |
| | 40 | * set non-stop mode |
| | 41 | */ |
| | 42 | void os_nonstop_mode(int flag) |
| | 43 | { |
| | 44 | } |
| | 45 | |
| | 46 | /* |
| | 47 | * flush output |
| | 48 | */ |
| | 49 | void os_flush() |
| | 50 | { |
| | 51 | } |
| | 52 | |
| | 53 | /* |
| | 54 | * update the display |
| | 55 | */ |
| | 56 | void os_update_display() |
| | 57 | { |
| | 58 | } |
| | 59 | |
| | 60 | /* |
| | 61 | * get highlighting character - indicate that highlighting is to be |
| | 62 | * ignored |
| | 63 | */ |
| | 64 | /* set text attributes */ |
| | 65 | void os_set_text_attr(int /*attr*/) |
| | 66 | { |
| | 67 | /* not supported - ignore it */ |
| | 68 | } |
| | 69 | |
| | 70 | /* set text color */ |
| | 71 | void os_set_text_color(os_color_t /*fg*/, os_color_t /*bg*/) |
| | 72 | { |
| | 73 | /* not supported - ignore it */ |
| | 74 | } |
| | 75 | |
| | 76 | /* set the screen color */ |
| | 77 | void os_set_screen_color(os_color_t /*color*/) |
| | 78 | { |
| | 79 | /* not supported - ignore it */ |
| | 80 | } |
| | 81 | |
| | 82 | /* |
| | 83 | * display text |
| | 84 | */ |
| | 85 | void os_printz(const char *str) |
| | 86 | { |
| | 87 | /* suppress output if we're not in plain text mode */ |
| | 88 | if (S_status_mode == 0) |
| | 89 | fputs(str, stdout); |
| | 90 | } |
| | 91 | |
| | 92 | /* |
| | 93 | * get an event |
| | 94 | */ |
| | 95 | int os_get_event(unsigned long timeout, int use_timeout, |
| | 96 | os_event_info_t *info) |
| | 97 | { |
| | 98 | /* we can't handle timeouts */ |
| | 99 | if (use_timeout) |
| | 100 | return OS_EVT_NOTIMEOUT; |
| | 101 | |
| | 102 | /* get a key */ |
| | 103 | info->key[0] = os_getc_raw(); |
| | 104 | if (info->key[0] == 0) |
| | 105 | info->key[1] = os_getc_raw(); |
| | 106 | |
| | 107 | /* return the keyboard event */ |
| | 108 | return OS_EVT_KEY; |
| | 109 | } |
| | 110 | |
| | 111 | /* |
| | 112 | * clear the screen |
| | 113 | */ |
| | 114 | void oscls() |
| | 115 | { |
| | 116 | /* |
| | 117 | * print a ^L to clear the screen, if such sequences are interpreted |
| | 118 | * by the display driver or terminal |
| | 119 | */ |
| | 120 | printf("\014\n"); |
| | 121 | } |
| | 122 | |
| | 123 | /* |
| | 124 | * read from the keyboard |
| | 125 | */ |
| | 126 | unsigned char *os_gets(unsigned char *buf, size_t buflen) |
| | 127 | { |
| | 128 | size_t len; |
| | 129 | |
| | 130 | /* read from stdin; return failure if fgets does */ |
| | 131 | if (fgets((char *)buf, buflen, stdin) == 0) |
| | 132 | return 0; |
| | 133 | |
| | 134 | /* remove the trailing newline in the result, if there is one */ |
| | 135 | if ((len = strlen((char *)buf)) != 0 && buf[len-1] == '\n') |
| | 136 | buf[len-1] = '\0'; |
| | 137 | |
| | 138 | /* return the buffer pointer */ |
| | 139 | return buf; |
| | 140 | } |
| | 141 | |
| | 142 | /* |
| | 143 | * read from keyboard with timeout |
| | 144 | */ |
| | 145 | int os_gets_timeout(unsigned char *buf, size_t bufl, |
| | 146 | unsigned long timeout_in_milliseconds, int use_timeout) |
| | 147 | { |
| | 148 | /* |
| | 149 | * if we've been asked to read with a timeout, we can't comply, so |
| | 150 | * just return the no-timeout-available error code; if we're reading |
| | 151 | * without a timeout, just use the ordinary input reader |
| | 152 | */ |
| | 153 | if (use_timeout) |
| | 154 | { |
| | 155 | /* timeout requested, no can do - return the no-timeout error code */ |
| | 156 | return OS_EVT_NOTIMEOUT; |
| | 157 | } |
| | 158 | else |
| | 159 | { |
| | 160 | /* |
| | 161 | * no timeout requested, so use the ordinary reader, and translate |
| | 162 | * its returns codes to the appropriate equivalents for this |
| | 163 | * routine (null from os_gets -> error -> OS_EVT_EOF as our return |
| | 164 | * code; non-null -> success -> OS_EVT_LINE) |
| | 165 | */ |
| | 166 | return (os_gets(buf, bufl) == 0 ? OS_EVT_EOF : OS_EVT_LINE); |
| | 167 | } |
| | 168 | } |
| | 169 | |
| | 170 | /* |
| | 171 | * Cancel interrupted input. We don't handle input with timeout in the |
| | 172 | * first place, so interrupted input is impossible, so we need do nothing |
| | 173 | * here. |
| | 174 | */ |
| | 175 | void os_gets_cancel(int reset) |
| | 176 | { |
| | 177 | /* there's nothing for us to do */ |
| | 178 | } |
| | 179 | |
| | 180 | /* |
| | 181 | * prompt for information through a dialog |
| | 182 | */ |
| | 183 | int os_input_dialog(int /*icon_id*/, |
| | 184 | const char *prompt, int standard_button_set, |
| | 185 | const char **buttons, int button_count, |
| | 186 | int /*default_index*/, int /*cancel_index*/) |
| | 187 | { |
| | 188 | /* keep going until we get a valid response */ |
| | 189 | for (;;) |
| | 190 | { |
| | 191 | int i; |
| | 192 | char buf[256]; |
| | 193 | const char *p; |
| | 194 | const char *cur; |
| | 195 | char *resp; |
| | 196 | int match_cnt; |
| | 197 | int last_found; |
| | 198 | static const struct |
| | 199 | { |
| | 200 | const char *buttons[3]; |
| | 201 | int button_count; |
| | 202 | } std_btns[] = |
| | 203 | { |
| | 204 | { { "&OK" }, 1 }, |
| | 205 | { { "&OK", "&Cancel" }, 2 }, |
| | 206 | { { "&Yes", "&No" }, 2 }, |
| | 207 | { { "&Yes", "&No", "&Cancel" }, 3 } |
| | 208 | }; |
| | 209 | |
| | 210 | /* |
| | 211 | * if we have a standard button set selected, get our button |
| | 212 | * labels |
| | 213 | */ |
| | 214 | switch(standard_button_set) |
| | 215 | { |
| | 216 | case 0: |
| | 217 | /* use the explicit buttons provided */ |
| | 218 | break; |
| | 219 | |
| | 220 | case OS_INDLG_OK: |
| | 221 | i = 0; |
| | 222 | |
| | 223 | use_std_btns: |
| | 224 | /* use the selected standard button set */ |
| | 225 | buttons = (const char **)std_btns[i].buttons; |
| | 226 | button_count = std_btns[i].button_count; |
| | 227 | break; |
| | 228 | |
| | 229 | case OS_INDLG_OKCANCEL: |
| | 230 | i = 1; |
| | 231 | goto use_std_btns; |
| | 232 | |
| | 233 | case OS_INDLG_YESNO: |
| | 234 | i = 2; |
| | 235 | goto use_std_btns; |
| | 236 | |
| | 237 | case OS_INDLG_YESNOCANCEL: |
| | 238 | i = 3; |
| | 239 | goto use_std_btns; |
| | 240 | |
| | 241 | default: |
| | 242 | /* |
| | 243 | * we don't recognize other standard button sets - return an |
| | 244 | * error |
| | 245 | */ |
| | 246 | return 0; |
| | 247 | } |
| | 248 | |
| | 249 | /* |
| | 250 | * if there are no buttons defined, they'll never be able to |
| | 251 | * respond, so we'd just loop forever - rather than let that |
| | 252 | * happen, return failure |
| | 253 | */ |
| | 254 | if (button_count == 0) |
| | 255 | return 0; |
| | 256 | |
| | 257 | /* display a newline and the prompt string */ |
| | 258 | os_printz("\n"); |
| | 259 | os_printz(prompt); |
| | 260 | |
| | 261 | /* display the response */ |
| | 262 | for (i = 0 ; i < button_count ; ++i) |
| | 263 | { |
| | 264 | /* |
| | 265 | * display a slash to separate responses, if this isn't the |
| | 266 | * first one |
| | 267 | */ |
| | 268 | if (i != 0) |
| | 269 | os_printz("/"); |
| | 270 | |
| | 271 | /* get the current button */ |
| | 272 | cur = buttons[i]; |
| | 273 | |
| | 274 | /* |
| | 275 | * Look for a "&" in the response string. If we find it, |
| | 276 | * remove the "&" and enclose the shortcut key in parens. |
| | 277 | */ |
| | 278 | for (p = cur ; *p != '&' && *p != '\0' ; ++p) ; |
| | 279 | |
| | 280 | /* if we found the "&", put the next character in parens */ |
| | 281 | if (*p == '&') |
| | 282 | { |
| | 283 | size_t pre_len; |
| | 284 | size_t post_len; |
| | 285 | |
| | 286 | /* limit the prefix length to avoid overflowing the buffer */ |
| | 287 | pre_len = p - cur; |
| | 288 | if (pre_len > sizeof(buf) - 5) |
| | 289 | pre_len = sizeof(buf) - 5; |
| | 290 | |
| | 291 | /* limit the postfix length to avoid buffer overflow, too */ |
| | 292 | post_len = strlen(p + 2); |
| | 293 | if (post_len > sizeof(buf) - 5 - pre_len) |
| | 294 | post_len = sizeof(buf) - 5 - pre_len; |
| | 295 | |
| | 296 | /* reformat the response string */ |
| | 297 | sprintf(buf, "%.*s(%c)%.*s", |
| | 298 | (int)pre_len, cur, *(p + 1), (int)post_len, p + 2); |
| | 299 | |
| | 300 | /* show it */ |
| | 301 | os_printz(buf); |
| | 302 | } |
| | 303 | else |
| | 304 | { |
| | 305 | /* no '&' - just display the response string as-is */ |
| | 306 | os_printz(cur); |
| | 307 | } |
| | 308 | } |
| | 309 | |
| | 310 | /* read the response */ |
| | 311 | os_printz(" > "); |
| | 312 | os_gets((unsigned char *)buf, sizeof(buf)); |
| | 313 | |
| | 314 | /* skip any leading spaces in the reply */ |
| | 315 | for (resp = buf ; isspace(*resp) ; ++resp) ; |
| | 316 | |
| | 317 | /* if it's one character, check it against the shortcut keys */ |
| | 318 | if (strlen(resp) == 1) |
| | 319 | { |
| | 320 | /* scan the responses */ |
| | 321 | for (i = 0 ; i < button_count ; ++i) |
| | 322 | { |
| | 323 | /* look for a '&' in this button */ |
| | 324 | for (p = buttons[i] ; *p != '&' && *p != '\0' ; ++p) ; |
| | 325 | |
| | 326 | /* if we found the '&', check the shortcut */ |
| | 327 | if (*p == '&' && toupper(*(p+1)) == toupper(*resp)) |
| | 328 | { |
| | 329 | /* |
| | 330 | * this is the one - return the current index |
| | 331 | * (bumping it by one to get a 1-based value) |
| | 332 | */ |
| | 333 | return i + 1; |
| | 334 | } |
| | 335 | } |
| | 336 | } |
| | 337 | |
| | 338 | /* |
| | 339 | * Either it's not a one-character reply, or it didn't match a |
| | 340 | * short-cut - check it against the leading substrings of the |
| | 341 | * responses. If it matches exactly one of the responses in its |
| | 342 | * leading substring, use that response. |
| | 343 | */ |
| | 344 | for (i = 0, match_cnt = 0 ; i < button_count ; ++i) |
| | 345 | { |
| | 346 | const char *p1; |
| | 347 | const char *p2; |
| | 348 | |
| | 349 | /* |
| | 350 | * compare this response to the user's response; skip any |
| | 351 | * '&' in the button label |
| | 352 | */ |
| | 353 | for (p1 = resp, p2 = buttons[i] ; *p1 != '\0' && *p2 != '\0' ; |
| | 354 | ++p1, ++p2) |
| | 355 | { |
| | 356 | /* if this is a '&' in the button label, skip it */ |
| | 357 | if (*p2 == '&') |
| | 358 | ++p2; |
| | 359 | |
| | 360 | /* if these characters don't match, it's no match */ |
| | 361 | if (toupper(*p1) != toupper(*p2)) |
| | 362 | break; |
| | 363 | } |
| | 364 | |
| | 365 | /* |
| | 366 | * if we reached the end of the user's response, we have a |
| | 367 | * match in the leading substring - count it and remember |
| | 368 | * this as the last one, but keep looking, since we need to |
| | 369 | * make sure we don't have any other matches |
| | 370 | */ |
| | 371 | if (*p1 == '\0') |
| | 372 | { |
| | 373 | ++match_cnt; |
| | 374 | last_found = i; |
| | 375 | } |
| | 376 | } |
| | 377 | |
| | 378 | /* |
| | 379 | * if we found exactly one match, return it (adjusting to a |
| | 380 | * 1-based index); if we found more or less than one match, it's |
| | 381 | * not a valid response, so start over with a new prompt |
| | 382 | */ |
| | 383 | if (match_cnt == 1) |
| | 384 | return last_found + 1; |
| | 385 | } |
| | 386 | } |
| | 387 | |
| | 388 | /* |
| | 389 | * ask for a file |
| | 390 | */ |
| | 391 | int os_askfile(const char *prompt, char *reply, int replen, |
| | 392 | int /*dialog_type*/, os_filetype_t /*file_type*/) |
| | 393 | { |
| | 394 | /* show the prompt */ |
| | 395 | os_printz(prompt); |
| | 396 | os_printz(" >"); |
| | 397 | |
| | 398 | /* ask for the filename */ |
| | 399 | os_gets((unsigned char *)reply, replen); |
| | 400 | |
| | 401 | /* |
| | 402 | * if they entered an empty line, return "cancel"; otherwise, return |
| | 403 | * success |
| | 404 | */ |
| | 405 | return (reply[0] == '\0' ? OS_AFE_CANCEL : OS_AFE_SUCCESS); |
| | 406 | } |
| | 407 | |
| | 408 | /* |
| | 409 | * get system information |
| | 410 | */ |
| | 411 | int os_get_sysinfo(int code, void *param, long *result) |
| | 412 | { |
| | 413 | switch(code) |
| | 414 | { |
| | 415 | case SYSINFO_HTML: |
| | 416 | case SYSINFO_JPEG: |
| | 417 | case SYSINFO_PNG: |
| | 418 | case SYSINFO_WAV: |
| | 419 | case SYSINFO_MIDI: |
| | 420 | case SYSINFO_WAV_MIDI_OVL: |
| | 421 | case SYSINFO_WAV_OVL: |
| | 422 | case SYSINFO_PREF_IMAGES: |
| | 423 | case SYSINFO_PREF_SOUNDS: |
| | 424 | case SYSINFO_PREF_MUSIC: |
| | 425 | case SYSINFO_PREF_LINKS: |
| | 426 | case SYSINFO_MPEG: |
| | 427 | case SYSINFO_MPEG1: |
| | 428 | case SYSINFO_MPEG2: |
| | 429 | case SYSINFO_MPEG3: |
| | 430 | case SYSINFO_LINKS_HTTP: |
| | 431 | case SYSINFO_LINKS_FTP: |
| | 432 | case SYSINFO_LINKS_NEWS: |
| | 433 | case SYSINFO_LINKS_MAILTO: |
| | 434 | case SYSINFO_LINKS_TELNET: |
| | 435 | case SYSINFO_PNG_TRANS: |
| | 436 | case SYSINFO_PNG_ALPHA: |
| | 437 | case SYSINFO_OGG: |
| | 438 | case SYSINFO_TEXT_HILITE: |
| | 439 | case SYSINFO_TEXT_COLORS: |
| | 440 | case SYSINFO_BANNERS: |
| | 441 | /* |
| | 442 | * we don't support any of these features - set the result to 0 |
| | 443 | * to indicate this |
| | 444 | */ |
| | 445 | *result = 0; |
| | 446 | |
| | 447 | /* return true to indicate that we recognized the code */ |
| | 448 | return TRUE; |
| | 449 | |
| | 450 | case SYSINFO_INTERP_CLASS: |
| | 451 | /* indicate that we're a text-only interpreter */ |
| | 452 | *result = SYSINFO_ICLASS_TEXT; |
| | 453 | return TRUE; |
| | 454 | |
| | 455 | default: |
| | 456 | /* we don't recognize other codes */ |
| | 457 | return FALSE; |
| | 458 | } |
| | 459 | } |
| | 460 | |
| | 461 | /* |
| | 462 | * set status mode |
| | 463 | */ |
| | 464 | void os_status(int mode) |
| | 465 | { |
| | 466 | /* remember the new mode */ |
| | 467 | S_status_mode = mode; |
| | 468 | } |
| | 469 | |
| | 470 | /* |
| | 471 | * display a string in the right half of the status line |
| | 472 | */ |
| | 473 | void os_strsc(const char *) |
| | 474 | { |
| | 475 | /* ignore it - we don't have a status line in this UI */ |
| | 476 | } |
| | 477 | |
| | 478 | void os_plain() |
| | 479 | { |
| | 480 | /* |
| | 481 | * this implementation is always in plain mode, so there's nothing |
| | 482 | * we need to do here |
| | 483 | */ |
| | 484 | } |
| | 485 | |
| | 486 | void os_expause() |
| | 487 | { |
| | 488 | /* do nothing */ |
| | 489 | } |
| | 490 | |
| | 491 | /* ------------------------------------------------------------------------ */ |
| | 492 | /* |
| | 493 | * none of the banner functions are useful in plain stdio mode |
| | 494 | */ |
| | 495 | void *os_banner_create(void *parent, int where, void *other, int wintype, |
| | 496 | int align, int siz, int siz_units, unsigned long style) |
| | 497 | { |
| | 498 | return 0; |
| | 499 | } |
| | 500 | |
| | 501 | void os_banner_delete(void *banner_handle) |
| | 502 | { |
| | 503 | } |
| | 504 | |
| | 505 | void os_banner_orphan(void *banner_handle) |
| | 506 | { |
| | 507 | } |
| | 508 | |
| | 509 | void os_banner_disp(void *banner_handle, const char *txt, size_t len) |
| | 510 | { |
| | 511 | } |
| | 512 | |
| | 513 | void os_banner_flush(void *banner_handle) |
| | 514 | { |
| | 515 | } |
| | 516 | |
| | 517 | void os_banner_set_size(void *banner_handle, int siz, int siz_units, |
| | 518 | int is_advisory) |
| | 519 | { |
| | 520 | } |
| | 521 | |
| | 522 | void os_banner_size_to_contents(void *banner_handle) |
| | 523 | { |
| | 524 | } |
| | 525 | |
| | 526 | void os_banner_start_html(void *banner_handle) |
| | 527 | { |
| | 528 | } |
| | 529 | |
| | 530 | void os_banner_end_html(void *banner_handle) |
| | 531 | { |
| | 532 | } |
| | 533 | |
| | 534 | void os_banner_set_attr(void *banner_handle, int attr) |
| | 535 | { |
| | 536 | } |
| | 537 | |
| | 538 | void os_banner_set_color(void *banner_handle, os_color_t fg, os_color_t bg) |
| | 539 | { |
| | 540 | } |
| | 541 | |
| | 542 | void os_banner_set_screen_color(void *banner_handle, os_color_t color) |
| | 543 | { |
| | 544 | } |
| | 545 | |
| | 546 | void os_banner_clear(void *banner_handle) |
| | 547 | { |
| | 548 | } |
| | 549 | |
| | 550 | int os_banner_get_charwidth(void *banner_handle) |
| | 551 | { |
| | 552 | return 0; |
| | 553 | } |
| | 554 | |
| | 555 | int os_banner_get_charheight(void *banner_handle) |
| | 556 | { |
| | 557 | return 0; |
| | 558 | } |
| | 559 | |
| | 560 | int os_banner_getinfo(void *banner_handle, os_banner_info_t *info) |
| | 561 | { |
| | 562 | return FALSE; |
| | 563 | } |
| | 564 | |
| | 565 | void os_banner_goto(void *banner_handle, int row, int col) |
| | 566 | { |
| | 567 | } |
| | 568 | |