cfad47cfa3/tads3/vmbiftio.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header$";
4
#endif
5
6
/* 
7
 *   Copyright (c) 2000, 2002 Michael J. Roberts.  All Rights Reserved.
8
 *   
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
10
 *   on using and copying this software.  
11
 */
12
/*
13
Name
14
  vmbiftio.cpp - TADS Input/Output functions
15
Function
16
  
17
Notes
18
  
19
Modified
20
  02/08/00 MJRoberts  - Creation
21
*/
22
23
#include <stdio.h>
24
#include <string.h>
25
#include <time.h>
26
27
#include "t3std.h"
28
#include "os.h"
29
#include "utf8.h"
30
#include "charmap.h"
31
#include "vmbiftio.h"
32
#include "vmstack.h"
33
#include "vmerr.h"
34
#include "vmerrnum.h"
35
#include "vmglob.h"
36
#include "vmpool.h"
37
#include "vmobj.h"
38
#include "vmstr.h"
39
#include "vmlst.h"
40
#include "vmrun.h"
41
#include "vmfile.h"
42
#include "vmconsol.h"
43
#include "vmstrres.h"
44
#include "vmvsn.h"
45
#include "vmhost.h"
46
#include "vmpredef.h"
47
#include "vmcset.h"
48
#include "vmfilobj.h"
49
50
51
/* ------------------------------------------------------------------------ */
52
/*
53
 *   Display a value 
54
 */
55
void CVmBifTIO::say(VMG_ uint argc)
56
{
57
    /* write the value to the main console */
58
    say_to_console(vmg_ G_console, argc);
59
}
60
61
/*
62
 *   Display the value or values at top of stack to the given console 
63
 */
64
void CVmBifTIO::say_to_console(VMG_ CVmConsole *console, uint argc)
65
{
66
    vm_val_t val;
67
    const char *str;
68
    char buf[30];
69
    size_t len;
70
    vm_val_t new_str;
71
72
    /* presume we won't need to create a new string value */
73
    new_str.set_nil();
74
75
    /* display each argument */
76
    for ( ; argc != 0 ; --argc)
77
    {
78
        /* get our argument */
79
        G_stk->pop(&val);
80
81
        /* see what we have */
82
        switch(val.typ)
83
        {
84
        case VM_SSTRING:
85
            /* get the string */
86
            str = G_const_pool->get_ptr(val.val.ofs);
87
88
            /* the original value is our string */
89
            new_str = val;
90
91
        disp_str:
92
            /* push the string to protect from garbage collection */
93
            G_stk->push(&new_str);
94
95
            /* display the string through the output formatter */
96
            console->format_text(vmg_ str + 2, osrp2(str));
97
98
            /* discard the saved string now that we no longer need it */
99
            G_stk->discard();
100
101
            /* done */
102
            break;
103
104
        case VM_OBJ:
105
            /* convert it to a string */
106
            str = vm_objp(vmg_ val.val.obj)
107
                  ->cast_to_string(vmg_ val.val.obj, &new_str);
108
109
            /* go display it */
110
            goto disp_str;
111
112
        case VM_INT:
113
            /* convert it to a string */
114
            sprintf(buf + 2, "%ld", val.val.intval);
115
116
            /* set its length */
117
            len = strlen(buf + 2);
118
            oswp2(buf, len);
119
120
            /* display it */
121
            str = buf;
122
            goto disp_str;
123
124
        case VM_NIL:
125
            /* display nothing */
126
            break;
127
128
        default:
129
            /* other types are invalid */
130
            err_throw(VMERR_BAD_TYPE_BIF);
131
        }
132
    }
133
}
134
135
/* ------------------------------------------------------------------------ */
136
/*
137
 *   Logging - turn on or off output text capture 
138
 */
139
140
#define LOG_SCRIPT   1
141
#define LOG_CMD      2
142
#define LOG_EVENT    3
143
144
void CVmBifTIO::logging(VMG_ uint argc)
145
{
146
    vm_val_t fname_val;
147
    int log_type;
148
    
149
    /* check arguments */
150
    check_argc_range(vmg_ argc, 1, 2);
151
152
    /* retrieve the filename argument */
153
    G_stk->pop(&fname_val);
154
155
    /* get the log type argument, if present */
156
    log_type = (argc >= 2 ? pop_int_val(vmg0_) : LOG_SCRIPT);
157
158
    /* 
159
     *   if they passed us nil, turn off logging; otherwise, start logging
160
     *   to the filename given by the string 
161
     */
162
    if (fname_val.typ == VM_NIL)
163
    {
164
        /* turn off the appropriate type of logging */
165
        switch(log_type)
166
        {
167
        case LOG_SCRIPT:
168
            G_console->close_log_file();
169
            break;
170
171
        case LOG_CMD:
172
        case LOG_EVENT:
173
            G_console->close_command_log();
174
            break;
175
176
        default:
177
            err_throw(VMERR_BAD_VAL_BIF);
178
        }
179
    }
180
    else
181
    {
182
        char fname[OSFNMAX];
183
        
184
        /* 
185
         *   get the filename string (converted to the file system
186
         *   character set) 
187
         */
188
        G_stk->push(&fname_val);
189
        pop_str_val_fname(vmg_ fname, sizeof(fname));
190
191
        /* open the appropriate log file */
192
        switch(log_type)
193
        {
194
        case LOG_SCRIPT:
195
            G_console->open_log_file(fname);
196
            break;
197
198
        case LOG_CMD:
199
        case LOG_EVENT:
200
            G_console->open_command_log(fname, log_type == LOG_EVENT);
201
            break;
202
203
        default:
204
            err_throw(VMERR_BAD_VAL_BIF);
205
        }
206
    }
207
}
208
209
/* ------------------------------------------------------------------------ */
210
/*
211
 *   clearscreen - clear the main display screen
212
 */
213
void CVmBifTIO::clearscreen(VMG_ uint argc)
214
{
215
    /* check arguments */
216
    check_argc(vmg_ argc, 0);
217
218
    /* ask the console to clear the screen */
219
    G_console->clear_window(vmg0_);
220
}
221
222
/* ------------------------------------------------------------------------ */
223
/*
224
 *   more - show MORE prompt
225
 */
226
void CVmBifTIO::more(VMG_ uint argc)
227
{
228
    /* check arguments */
229
    check_argc(vmg_ argc, 0);
230
231
    /* 
232
     *   if we're reading from a script, ignore this request - these types of
233
     *   interactive pauses are irrelevant when reading a script, since we're
234
     *   getting our input non-interactively 
235
     */
236
    if (G_console->is_reading_script())
237
        return;
238
239
    /* flush the display output */
240
    G_console->flush_all(vmg_ VM_NL_NONE);
241
242
    /* show the MORE prompt */
243
    G_console->show_more_prompt(vmg0_);
244
}
245
246
/* ------------------------------------------------------------------------ */
247
/*
248
 *   input - get a line of input from the keyboard
249
 */
250
void CVmBifTIO::input(VMG_ uint argc)
251
{
252
    char buf[256];
253
    
254
    /* check arguments */
255
    check_argc(vmg_ argc, 0);
256
257
    /* read a line of text from the keyboard */
258
    if (G_console->read_line(vmg_ buf, sizeof(buf)))
259
    {
260
        /* end of file - return nil */
261
        retval_nil(vmg0_);
262
    }
263
    else
264
    {
265
        /* return the string */
266
        retval_str(vmg_ buf);
267
    }
268
}
269
270
/* ------------------------------------------------------------------------ */
271
/*
272
 *   inputkey - read a keystroke
273
 */
274
void CVmBifTIO::inputkey(VMG_ uint argc)
275
{
276
    char buf[32];
277
    char c[10];
278
    size_t len;
279
    int evt;
280
    static const int filter[] = { OS_EVT_KEY };
281
282
    /* check arguments */
283
    check_argc(vmg_ argc, 0);
284
285
    /* check for script input */
286
    if (G_console->read_event_script(
287
        vmg_ &evt, buf, sizeof(buf),
288
        filter, sizeof(filter)/sizeof(filter[0]), 0))
289
    {
290
        /* we got a key from the script */
291
        retval_ui_str(vmg_ buf);
292
293
        /* log the event */
294
        G_console->log_event(vmg_ OS_EVT_KEY, buf, strlen(buf), FALSE);
295
296
        /* done */
297
        return;
298
    }
299
300
    /* flush any output */
301
    G_console->flush_all(vmg_ VM_NL_INPUT);
302
    
303
    /* get a keystroke */
304
    c[0] = (char)os_getc_raw();
305
    len = 1;
306
    
307
    /* if it's an extended key, map it specially */
308
    if (c[0] == 0)
309
    {
310
        char extc;
311
        
312
        /* get the second part of the sequence */
313
        extc = (char)os_getc_raw();
314
        
315
        /* map the extended key */
316
        map_ext_key(vmg_ buf, (unsigned char)extc);
317
    }
318
    else
319
    {
320
        /* continue fetching bytes until we have a full character */
321
        while (!raw_key_complete(vmg_ c, len) && len + 1 < sizeof(c))
322
        {
323
            /* 
324
             *   We don't yet have enough bytes for a complete character, so
325
             *   read another raw byte.  The keyboard driver should already
326
             *   have queued up all of the bytes we need to complete the
327
             *   character sequence, so there should never be a delay from
328
             *   os_getc_raw() here - it should simply return the next byte
329
             *   of the sequence immediately.  
330
             */
331
            c[len++] = (char)os_getc_raw();
332
        }
333
        c[len] = '\0';
334
        
335
        /* 
336
         *   translate the key from the local character set to UTF-8, and map
337
         *   the extended key code to the portable representation 
338
         */
339
        map_raw_key(vmg_ buf, c, len);
340
    }
341
    
342
    /* reset the [MORE] counter */
343
    G_console->reset_line_count(FALSE);
344
    
345
    /* log the event */
346
    G_console->log_event(vmg_ OS_EVT_KEY, buf, strlen(buf), TRUE);
347
    
348
    /* return the string */
349
    retval_str(vmg_ buf);
350
}
351
352
/* ------------------------------------------------------------------------ */
353
/*
354
 *   inputevent - read an event
355
 */
356
void CVmBifTIO::inputevent(VMG_ uint argc)
357
{
358
    int use_timeout;
359
    unsigned long timeout;
360
    os_event_info_t info;
361
    int evt;
362
    int ele_count;
363
    vm_obj_id_t lst_obj;
364
    CVmObjList *lst;
365
    char keyname[32];
366
    vm_val_t val;
367
    int from_script = FALSE;
368
    static const int filter[] =
369
    {
370
        OS_EVT_KEY, OS_EVT_TIMEOUT, OS_EVT_NOTIMEOUT,
371
        OS_EVT_HREF, OS_EVT_EOF, OS_EVT_COMMAND
372
    };
373
374
    /* check arguments */
375
    check_argc_range(vmg_ argc, 0, 1);
376
377
    /* if there's a timeout argument, get it */
378
    if (argc == 0)
379
    {
380
        /* there's no timeout */
381
        use_timeout = FALSE;
382
        timeout = 0;
383
    }
384
    else if (G_stk->get(0)->typ == VM_NIL)
385
    {
386
        /* the timeout is nil, which is the same as no timeout */
387
        use_timeout = FALSE;
388
        timeout = 0;
389
390
        /* discard the nil timeout value */
391
        G_stk->discard();
392
    }
393
    else
394
    {
395
        /* pop the timeout value */
396
        timeout = pop_long_val(vmg0_);
397
398
        /* note that we have a timeout to use */
399
        use_timeout = TRUE;
400
    }
401
402
    /* check for script input */
403
    if (G_console->read_event_script(
404
        vmg_ &evt, info.href, sizeof(info.href),
405
        filter, sizeof(filter)/sizeof(filter[0]), 0))
406
    {
407
        /* we got a script event - note it */
408
        from_script = TRUE;
409
410
        /* translate certain events */
411
        switch (evt)
412
        {
413
        case OS_EVT_COMMAND:
414
            /* read the numeric parameter */
415
            info.cmd_id = atoi(info.href);
416
            break;
417
        }
418
    }
419
    else
420
    {
421
        /* flush any buffered output */
422
        G_console->flush_all(vmg_ VM_NL_INPUT);
423
424
        /* reset the [MORE] counter */
425
        G_console->reset_line_count(FALSE);
426
427
        /* read an event from the OS layer */
428
        evt = os_get_event(timeout, use_timeout, &info);
429
    }
430
431
    /* figure out how big a list we need to allocate */
432
    switch(evt)
433
    {
434
    case OS_EVT_KEY:
435
        /* 
436
         *   we need two elements - one for the event type code, one for the
437
         *   keystroke string 
438
         */
439
        ele_count = 2;
440
        break;
441
442
    case OS_EVT_COMMAND:
443
        /* we need a second element for the command ID */
444
        ele_count = 2;
445
        break;
446
447
    case OS_EVT_HREF:
448
        /* 
449
         *   we need two elements - one for the event type code, one for the
450
         *   HREF string 
451
         */
452
        ele_count = 2;
453
        break;
454
455
    default:
456
        /* for anything else, we need only the event type code element */
457
        ele_count = 1;
458
        break;
459
    }
460
461
    /* create the return list */
462
    lst_obj = CVmObjList::create(vmg_ FALSE, ele_count);
463
    lst = (CVmObjList *)vm_objp(vmg_ lst_obj);
464
465
    /* save the list on the stack to protect against garbage collection */
466
    val.set_obj(lst_obj);
467
    G_stk->push(&val);
468
469
    /* fill in the first element with the event type code */
470
    val.set_int(evt);
471
    lst->cons_set_element(0, &val);
472
473
    /* set additional elements, according to the event type */
474
    switch(evt)
475
    {
476
    case OS_EVT_KEY:
477
        /* map the extended or ordinary key, as appropriate */
478
        if (from_script)
479
        {
480
            /* we got a key from the script - it's in the 'href' field */
481
            val.set_obj(str_from_ui_str(vmg_ info.href));
482
483
            /* log the event */
484
            G_console->log_event(
485
                vmg_ OS_EVT_KEY, info.href, strlen(info.href), FALSE);
486
        }
487
        else if (info.key[0] == 0)
488
        {
489
            /* it's an extended key */
490
            map_ext_key(vmg_ keyname, info.key[1]);
491
492
            /* create a string for the key name */
493
            val.set_obj(CVmObjString::create(
494
                vmg_ FALSE, keyname, strlen(keyname)));
495
496
            /* log the event */
497
            G_console->log_event(
498
                vmg_ OS_EVT_KEY, keyname, strlen(keyname), TRUE);
499
        }
500
        else
501
        {
502
            char c[4];
503
            size_t len;
504
505
            /* fetch more bytes until we have a complete character */
506
            for (c[0] = (char)info.key[0], len = 1 ;
507
                 !raw_key_complete(vmg_ c, len) && len < sizeof(c) ; )
508
            {
509
                /* 
510
                 *   Read another input event.  The keyboard driver should
511
                 *   already have queued up all of the bytes needed to
512
                 *   complete this character sequence, so there should never
513
                 *   be a delay from os_get_event() here - it should simply
514
                 *   return immediately with another OS_EVT_KEY event with
515
                 *   the next byte of the sequence.  
516
                 */
517
                evt = os_get_event(0, FALSE, &info);
518
519
                /* 
520
                 *   if it's not a keystroke event, something's wrong -
521
                 *   ignore the event and stop trying to read the remaining
522
                 *   bytes of the character sequence 
523
                 */
524
                if (evt != OS_EVT_KEY)
525
                    break;
526
527
                /* store the next byte of the sequence */
528
                c[len++] = (char)info.key[0];
529
            }
530
531
            /* it's an ordinary key - map it */
532
            map_raw_key(vmg_ keyname, c, len);
533
534
            /* create a string for the key name */
535
            val.set_obj(CVmObjString::create(
536
                vmg_ FALSE, keyname, strlen(keyname)));
537
538
            /* log the event */
539
            G_console->log_event(
540
                vmg_ OS_EVT_KEY, keyname, strlen(keyname), TRUE);
541
        }
542
543
        /* add it to the list */
544
        lst->cons_set_element(1, &val);
545
546
        break;
547
548
    case OS_EVT_HREF:
549
        /* create the string for the href text */
550
        val.set_obj(str_from_ui_str(vmg_ info.href));
551
552
        /* add it to the list */
553
        lst->cons_set_element(1, &val);
554
555
        /* log it */
556
        G_console->log_event(vmg_ OS_EVT_HREF,
557
                             info.href, strlen(info.href), FALSE);
558
        break;
559
560
    case OS_EVT_COMMAND:
561
        /* the second element is the command ID code */
562
        val.set_int(info.cmd_id);
563
        lst->cons_set_element(1, &val);
564
565
        /* log it */
566
        {
567
            char buf[20];
568
            sprintf(buf, "%d", info.cmd_id);
569
            G_console->log_event(vmg_ OS_EVT_COMMAND,
570
                                 buf, strlen(buf), TRUE);
571
        }
572
        break;
573
574
    default:
575
        /* other event types have no extra data */
576
        G_console->log_event(vmg_ evt);
577
        break;
578
    }
579
580
    /* return the list */
581
    retval_obj(vmg_ lst_obj);
582
583
    /* we can drop the garbage collection protection now */
584
    G_stk->discard();
585
}
586
587
/* ------------------------------------------------------------------------ */
588
/*
589
 *   Service routine: Map an "extended" keystroke from raw os_getc_raw()
590
 *   notation to a UTF-8 key name.  The caller should pass the second byte of
591
 *   the extended two-byte raw sequence.  
592
 */
593
int CVmBifTIO::map_ext_key(VMG_ char *namebuf, int extc)
594
{
595
    /*
596
     *   Portable key names for the extended keystrokes.  We map the extended
597
     *   key codes to these strings, so that the TADS code can access arrow
598
     *   keys and the like.  
599
     */
600
    static const char *ext_key_names[] =
601
    {
602
        "[up]",                                               /* CMD_UP - 1 */
603
        "[down]",                                           /* CMD_DOWN - 2 */
604
        "[right]",                                         /* CMD_RIGHT - 3 */
605
        "[left]",                                           /* CMD_LEFT - 4 */
606
        "[end]",                                             /* CMD_END - 5 */
607
        "[home]",                                           /* CMD_HOME - 6 */
608
        "[del-eol]",                                        /* CMD_DEOL - 7 */
609
        "[del-line]",                                       /* CMD_KILL - 8 */
610
        "[del]",                                             /* CMD_DEL - 9 */
611
        "[scroll]",                                         /* CMD_SCR - 10 */
612
        "[page up]",                                       /* CMD_PGUP - 11 */
613
        "[page down]",                                     /* CMD_PGDN - 12 */
614
        "[top]",                                            /* CMD_TOP - 13 */
615
        "[bottom]",                                         /* CMD_BOT - 14 */
616
        "[f1]",                                              /* CMD_F1 - 15 */
617
        "[f2]",                                              /* CMD_F2 - 16 */
618
        "[f3]",                                              /* CMD_F3 - 17 */
619
        "[f4]",                                              /* CMD_F4 - 18 */
620
        "[f5]",                                              /* CMD_F5 - 19 */
621
        "[f6]",                                              /* CMD_F6 - 20 */
622
        "[f7]",                                              /* CMD_F7 - 21 */
623
        "[f8]",                                              /* CMD_F8 - 22 */
624
        "[f9]",                                              /* CMD_F9 - 23 */
625
        "[f10]",                                            /* CMD_F10 - 24 */
626
        "[?]",                              /* invalid key - CMD_CHOME - 25 */
627
        "[tab]",                                            /* CMD_TAB - 26 */
628
        "[?]",                               /* invalid key - shift-F2 - 27 */
629
        "[?]",                                 /* not used (obsoleted) - 28 */
630
        "[word-left]",                                /* CMD_WORD_LEFT - 29 */
631
        "[word-right]",                              /* CMD_WORD_RIGHT - 30 */
632
        "[del-word]",                                  /* CMD_WORDKILL - 31 */
633
        "[eof]",                                            /* CMD_EOF - 32 */
634
        "[break]"                                         /* CMD_BREAK - 33 */
635
    };
636
637
    /* if it's in the key name array, use the array entry */
638
    if (extc >= 1
639
        && extc <= (int)sizeof(ext_key_names)/sizeof(ext_key_names[0]))
640
    {
641
        /* use the array name */
642
        strcpy(namebuf, ext_key_names[extc - 1]);
643
        return TRUE;
644
    }
645
646
    /* if it's in the ALT key range, generate an ALT key name */
647
    if ((unsigned char)extc >= CMD_ALT && (unsigned char)extc <= CMD_ALT + 25)
648
    {
649
        /* generate an ALT key name */
650
        strcpy(namebuf, "[alt-?]");
651
        namebuf[5] = (char)(extc - CMD_ALT + 'a');
652
        return TRUE;
653
    }
654
655
    /* it's not a valid key - use '[?]' as the name */
656
    strcpy(namebuf, "[?]");
657
    return FALSE;
658
}
659
660
/*
661
 *   Service routine: Map a keystroke from the raw notation, consisting of a
662
 *   normal keystroke in the local character set or an extended command key
663
 *   using a CMD_xxx code, to UTF-8.  If the keystroke is a control character
664
 *   or any CMD_xxx code, we'll map the key to a high-level keystroke name
665
 *   enclosed in square brackets.  
666
 */
667
int CVmBifTIO::map_raw_key(VMG_ char *namebuf, const char *c, size_t len)
668
{
669
    size_t outlen;
670
671
    /* if it's a control character, give it a portable key name */
672
    if (len == 1 && ((c[0] >= 1 && c[0] <= 27) || c[0] == 127))
673
    {
674
        switch(c[0])
675
        {
676
        case 10:
677
        case 13:
678
            /* 
679
             *   return an ASCII 10 (regardless of local newline conventions
680
             *   - this is the internal string representation, which we
681
             *   define to use ASCII 10 to represent a newline everywhere) 
682
             */
683
            namebuf[0] = 10;
684
            namebuf[1] = '\0';
685
            return TRUE;
686
687
        case 9:
688
            /* return ASCII 9 for TAB characters */
689
            namebuf[0] = 9;
690
            namebuf[1] = '\0';
691
            return TRUE;
692
693
        case 8:
694
        case 127:
695
            /* return '[bksp]' for backspace/del characters */
696
            strcpy(namebuf, "[bksp]");
697
            return TRUE;
698
699
        case 27:
700
            /* return '[esc]' for the escape key */
701
            strcpy(namebuf, "[esc]");
702
            return TRUE;
703
704
        default:
705
            /* return '[ctrl-X]' for other control characters */
706
            strcpy(namebuf, "[ctrl-?]");
707
            namebuf[6] = (char)(c[0] + 'a' - 1);
708
            return TRUE;
709
        }
710
    }
711
712
    /* map the character to wide Unicode */
713
    outlen = 32;
714
    G_cmap_from_ui->map(&namebuf, &outlen, c, len);
715
716
    /* null-terminate the result */
717
    *namebuf = '\0';
718
719
    /* successfully mapped */
720
    return TRUE;
721
}
722
723
/*
724
 *   Service routine: determine if a raw byte sequence forms a complete
725
 *   character in the local character set.  
726
 */
727
int CVmBifTIO::raw_key_complete(VMG_ const char *c, size_t len)
728
{
729
    /* ask the local character mapper if it's a complete character */
730
    return G_cmap_from_ui->is_complete_char(c, len);
731
}
732
733
734
/* ------------------------------------------------------------------------ */
735
/*
736
 *   Standard system button labels for bifinpdlg() 
737
 */
738
#define BIFINPDLG_LBL_OK      1
739
#define BIFINPDLG_LBL_CANCEL  2
740
#define BIFINPDLG_LBL_YES     3
741
#define BIFINPDLG_LBL_NO      4
742
743
/*
744
 *   inputdialog - run a dialog
745
 */
746
void CVmBifTIO::inputdialog(VMG_ uint argc)
747
{
748
    int icon_id;
749
    char prompt[256];
750
    char label_buf[256];
751
    vm_val_t label_val[10];
752
    const char *labels[10];
753
    int std_btns;
754
    int btn_cnt;
755
    const char *listp;
756
    char *dst;
757
    size_t dstrem;
758
    int default_resp;
759
    int cancel_resp;
760
    int resp;
761
    char numbuf[32];
762
    
763
    /* check arguments */
764
    check_argc(vmg_ argc, 5);
765
766
    /* get the icon number */
767
    icon_id = pop_int_val(vmg0_);
768
769
    /* get the prompt string */
770
    pop_str_val_ui(vmg_ prompt, sizeof(prompt));
771
772
    /* there aren't any buttons yet */
773
    btn_cnt = 0;
774
775
    /* check for the button type */
776
    if (G_stk->get(0)->typ == VM_INT)
777
    {
778
        /* get the standard button set ID */
779
        std_btns = pop_int_val(vmg0_);
780
    }
781
    else
782
    {
783
        size_t i;
784
        size_t cnt;
785
        vm_val_t *valp;
786
        
787
        /* we're not using any standard button set */
788
        std_btns = 0;
789
790
        /* get the button label list */
791
        listp = pop_list_val(vmg0_);
792
793
        /* 
794
         *   run through the list and get the button items into our array
795
         *   (we do this rather than traversing the list directly so that
796
         *   we don't have to worry about a constant list's data being
797
         *   paged out) 
798
         */
799
        cnt = vmb_get_len(listp);
800
801
        /* limit the number of elements to our private array size */
802
        if (cnt > sizeof(label_val)/sizeof(label_val[0]))
803
            cnt = sizeof(label_val)/sizeof(label_val[0]);
804
805
        /* skip the list length prefix */
806
        listp += VMB_LEN;
807
808
        /* copy the list */
809
        for (i = cnt, valp = label_val ; i > 0 ;
810
             --i, listp += VMB_DATAHOLDER, ++valp)
811
        {
812
            /* get this element into our array */
813
            vmb_get_dh(listp, valp);
814
        }
815
816
        /* set up to write into our label buffer */
817
        dst = label_buf;
818
        dstrem = sizeof(label_buf);
819
820
        /* now build our internal button list from the array elements */
821
        for (i = 0, valp = label_val ; i < cnt ; ++i, ++valp)
822
        {
823
            const char *p;
824
825
            /* 
826
             *   We could have a number or a string in each element.  If
827
             *   the element is a number, it refers to a standard label.
828
             *   If it's a string, use the string directly. 
829
             */
830
            if ((p = valp->get_as_string(vmg0_)) != 0)
831
            {
832
                size_t copy_len;
833
                
834
                /* 
835
                 *   it's a string - make a copy in the label buffer,
836
                 *   making sure to leave space for null termination 
837
                 */
838
                copy_len = vmb_get_len(p);
839
                if (copy_len > dstrem - 1)
840
                    copy_len = utf8_ptr::s_trunc(p + VMB_LEN, dstrem - 1);
841
                memcpy(dst, p + VMB_LEN, copy_len);
842
843
                /* null-terminate the buffer */
844
                dst[copy_len++] = '\0';
845
846
                /* set this button to point to the converted text */
847
                labels[btn_cnt++] = dst;
848
849
                /* skip past this label */
850
                dst += copy_len;
851
                dstrem -= copy_len;
852
            }
853
            else if (valp->typ == VM_INT)
854
            {
855
                int id;
856
                int resid;
857
                char rscbuf[128];
858
                
859
                /* it's a standard system label ID - get the ID */
860
                id = (int)valp->val.intval;
861
862
                /* translate it to the appropriate string resource */
863
                switch(id)
864
                {
865
                case BIFINPDLG_LBL_OK:
866
                    resid = VMRESID_BTN_OK;
867
                    break;
868
869
                case BIFINPDLG_LBL_CANCEL:
870
                    resid = VMRESID_BTN_CANCEL;
871
                    break;
872
873
                case BIFINPDLG_LBL_YES:
874
                    resid = VMRESID_BTN_YES;
875
                    break;
876
877
                case BIFINPDLG_LBL_NO:
878
                    resid = VMRESID_BTN_NO;
879
                    break;
880
881
                default:
882
                    resid = 0;
883
                    break;
884
                }
885
886
                /* 
887
                 *   if we got a valid resource ID, load the resource;
888
                 *   otherwise, skip this button 
889
                 */
890
                if (resid != 0
891
                    && !os_get_str_rsc(resid, rscbuf, sizeof(rscbuf)))
892
                {
893
                    /* set this button to point to the converted text */
894
                    labels[btn_cnt++] = dst;
895
896
                    /* convert the resource text to UTF-8 */
897
                    G_cmap_from_ui->map(&dst, &dstrem,
898
                                        rscbuf, strlen(rscbuf));
899
900
                    /* null-terminate the converted text */
901
                    *dst++ = '\0';
902
                    --dstrem;
903
                }
904
            }
905
        }
906
    }
907
908
    /* get the default response */
909
    if (G_stk->get(0)->typ == VM_NIL)
910
    {
911
        /* discard the nil argument */
912
        G_stk->discard();
913
914
        /* there's no default response */
915
        default_resp = 0;
916
    }
917
    else
918
    {
919
        /* get the default response index */
920
        default_resp = pop_int_val(vmg0_);
921
    }
922
923
    /* get the cancel response */
924
    if (G_stk->get(0)->typ == VM_NIL)
925
    {
926
        /* discard the nil argument */
927
        G_stk->discard();
928
929
        /* there's no cancel response */
930
        cancel_resp = 0;
931
    }
932
    else
933
    {
934
        /* get the cancel response index */
935
        cancel_resp = pop_int_val(vmg0_);
936
    }
937
938
    /* check for script input */
939
    static int filter[] = { VMCON_EVT_DIALOG };
940
    int evt;
941
    if (G_console->read_event_script(
942
        vmg_ &evt, numbuf, sizeof(numbuf),
943
        filter, sizeof(filter)/sizeof(filter[0]), 0))
944
    {
945
        /* we got a script response - no need to show the dialog */
946
        resp = atoi(numbuf);
947
948
        /* log the event */
949
        G_console->log_event(vmg_ VMCON_EVT_DIALOG,
950
                             numbuf, strlen(numbuf), FALSE);
951
    }
952
    else
953
    {
954
        /* flush output before showing the dialog */
955
        G_console->flush_all(vmg_ VM_NL_INPUT);
956
957
        /* show the dialog */
958
        resp = G_console->input_dialog(vmg_ icon_id, prompt,
959
                                       std_btns, labels, btn_cnt,
960
                                       default_resp, cancel_resp);
961
962
        /* log the event */
963
        sprintf(numbuf, "%d", resp);
964
        G_console->log_event(vmg_ VMCON_EVT_DIALOG,
965
                             numbuf, strlen(numbuf), TRUE);
966
    }
967
968
    /* return the result */
969
    retval_int(vmg_ resp);
970
}
971
972
/* ------------------------------------------------------------------------ */
973
/*
974
 *   askfile - ask for a filename via a standard file dialog
975
 */
976
void CVmBifTIO::askfile(VMG_ uint argc)
977
{
978
    char prompt[256];
979
    int dialog_type;
980
    os_filetype_t file_type;
981
    long flags;
982
    int result;
983
    char fname[OSFNMAX*3 + 1];
984
    vm_obj_id_t lst_obj;
985
    CVmObjList *lst;
986
    vm_val_t val;
987
    int from_script = FALSE;
988
    
989
    /* check arguments */
990
    check_argc(vmg_ argc, 4);
991
992
    /* get the prompt string */
993
    pop_str_val_ui(vmg_ prompt, sizeof(prompt));
994
995
    /* get the dialog type and file type */
996
    dialog_type = pop_int_val(vmg0_);
997
    file_type = (os_filetype_t)pop_int_val(vmg0_);
998
999
    /* pop the flags */
1000
    flags = pop_long_val(vmg0_);
1001
1002
    /* check for a script response */
1003
    static int filter[] = { VMCON_EVT_FILE };
1004
    int evt;
1005
    unsigned long attrs;
1006
    if (G_console->read_event_script(
1007
        vmg_ &evt, fname, sizeof(fname),
1008
        filter, sizeof(filter)/sizeof(filter[0]), &attrs))
1009
    {
1010
        char prompt[OSFNMAX + 255];
1011
        int ok = TRUE;
1012
        
1013
        /* we got a response from the script */
1014
        from_script = TRUE;
1015
        result = (fname[0] != '\0' ? OS_AFE_SUCCESS : OS_AFE_CANCEL);
1016
1017
        /* 
1018
         *   If this is a "save" prompt, and the OVERWRITE flag isn't
1019
         *   provided, and the file already exists, show an interactive
1020
         *   warning that we're about to ovewrite the file.  
1021
         */
1022
        if (dialog_type == OS_AFP_SAVE
1023
            && result == OS_AFE_SUCCESS
1024
            && (attrs & VMCON_EVTATTR_OVERWRITE) == 0
1025
            && !osfacc(fname))
1026
        {
1027
            /* the file exists - warn about the overwrite */
1028
            ok = FALSE;
1029
            sprintf(prompt, "The script might overwrite the file %s. ", fname);
1030
        }
1031
1032
        /* 
1033
         *   If this is a "save" prompt, AND the file doesn't already exist,
1034
         *   try creating the file to see if we can - this will test to see
1035
         *   if the target directory exists and is writable, for instance.  
1036
         */
1037
        if (ok
1038
            && dialog_type == OS_AFP_SAVE
1039
            && result == OS_AFE_SUCCESS
1040
            && osfacc(fname))
1041
        {
1042
            /* try creating the file */
1043
            osfildef *fp = osfopwb(fname, file_type);
1044
1045
            /* if that succeeded, undo the creation; otherwise warn */
1046
            if (fp != 0)
1047
            {
1048
                /* it worked - close and delete the test file */
1049
                osfcls(fp);
1050
                osfdel(fname);
1051
            }
1052
            else
1053
            {
1054
                /* didn't work - warn about it */
1055
                ok = FALSE;
1056
                sprintf(prompt, "The script is attempting to create file %s, "
1057
                        "but that file cannot be created.", fname);
1058
            }
1059
        }
1060
1061
        /*
1062
         *   If this is an "open" prompt, and the file isn't readable, warn
1063
         *   about it. 
1064
         */
1065
        if (ok
1066
            && dialog_type == OS_AFP_OPEN
1067
            && result == OS_AFE_SUCCESS
1068
            && osfacc(fname))
1069
        {
1070
            ok = FALSE;
1071
            sprintf(prompt, "The script is attempting to open file %s, "
1072
                    "but this file doesn't exist or isn't readable.", fname);
1073
        }
1074
1075
        /* check to see if we're warning about anything */
1076
        if (!ok)
1077
        {
1078
            char fullprompt[OSFNMAX + 255 + 150];
1079
1080
            /* build the full prompt */
1081
            sprintf(fullprompt, "%s Do you wish to proceed? Select Yes to "
1082
                    "proceed with this file, No to choose a different file, "
1083
                    "or Cancel to stop script playback.", prompt);
1084
1085
            /* 
1086
             *   display a dialog - note that this goes directly to user,
1087
             *   bypassing the active script, since this is a question about
1088
             *   how to handle a problem in the script 
1089
             */
1090
            switch (G_console->input_dialog(
1091
                vmg_ OS_INDLG_ICON_WARNING, fullprompt,
1092
                OS_INDLG_YESNOCANCEL, 0, 0, 2, 3))
1093
            {
1094
            case 1:
1095
                /* yes - proceed with fname */
1096
                break;
1097
1098
            case 2:
1099
                /* no - ask for a new file */
1100
                result = G_console->askfile(
1101
                    vmg_ prompt, strlen(prompt),
1102
                    fname, sizeof(fname), dialog_type, file_type);
1103
1104
                /* this didn't come from the script after all */
1105
                from_script = FALSE;
1106
1107
                /* if they canceled, cancel the whole script playback */
1108
                if (result != OS_AFE_SUCCESS)
1109
                    close_script_file(vmg0_);
1110
1111
                /* handled */
1112
                break;
1113
1114
            case 3:
1115
                /* cancel - stop the script playback */
1116
                close_script_file(vmg0_);
1117
1118
                /* indicate cancellation */
1119
                result = OS_AFE_CANCEL;
1120
                break;
1121
            }
1122
        }
1123
    }
1124
    else
1125
    {
1126
        /* ask for a file via the UI */
1127
        result = G_console->askfile(
1128
            vmg_ prompt, strlen(prompt),
1129
            fname, sizeof(fname), dialog_type, file_type);
1130
    }
1131
1132
    /* 
1133
     *   Allocate a list to store the return value.  If we successfully
1134
     *   got a filename, we need a two-element list - one element for the
1135
     *   success code and another for the string with the filename.  If we
1136
     *   didn't succeed in getting the filename, we only need a single
1137
     *   element, which will contain the error code. 
1138
     */
1139
    lst_obj = CVmObjList::create(vmg_ FALSE,
1140
                                 result == OS_AFE_SUCCESS ? 2 : 1);
1141
    lst = (CVmObjList *)vm_objp(vmg_ lst_obj);
1142
1143
    /* save it on the stack as protection against garbage collection */
1144
    val.set_obj(lst_obj);
1145
    G_stk->push(&val);
1146
1147
    /* set the first element to the result code */
1148
    val.set_int(result);
1149
    lst->cons_set_element(0, &val);
1150
1151
    /* if we got a filename, set the second element to the filename string */
1152
    if (result == OS_AFE_SUCCESS)
1153
    {
1154
        /* 
1155
         *   Create a string for the filename.  If it's coming from a script,
1156
         *   we need to translate it to the local character set; otherwise
1157
         *   it's already in UTF-8. 
1158
         */
1159
        val.set_obj(
1160
            from_script
1161
            ? str_from_ui_str(vmg_ fname)
1162
            : CVmObjString::create(vmg_ FALSE, fname, strlen(fname)));
1163
1164
        /* store the string as the second list element */
1165
        lst->cons_set_element(1, &val);
1166
    }
1167
1168
    /* log the event */
1169
    if (result == OS_AFE_SUCCESS)
1170
        G_console->log_event(vmg_ VMCON_EVT_FILE,
1171
                             fname, strlen(fname), FALSE);
1172
    else
1173
        G_console->log_event(vmg_ VMCON_EVT_FILE);
1174
1175
    /* return the list */
1176
    retval_obj(vmg_ lst_obj);
1177
1178
    /* we no longer need the garbage collector protection */
1179
    G_stk->discard();
1180
}
1181
1182
1183
/* ------------------------------------------------------------------------ */
1184
/*
1185
 *   timeDelay - pause for a specified interval
1186
 */
1187
void CVmBifTIO::timedelay(VMG_ uint argc)
1188
{
1189
    long delay_ms;
1190
    
1191
    /* check arguments */
1192
    check_argc(vmg_ argc, 1);
1193
1194
    /* get the delay time in milliseconds */
1195
    delay_ms = pop_long_val(vmg0_);
1196
1197
    /* flush any pending output */
1198
    G_console->flush_all(vmg_ VM_NL_NONE);
1199
1200
    /* ask the system code to pause */
1201
    os_sleep_ms(delay_ms);
1202
}
1203
1204
/* ------------------------------------------------------------------------ */
1205
/*
1206
 *   systemInfo
1207
 */
1208
void CVmBifTIO::sysinfo(VMG_ uint argc)
1209
{
1210
    int info;
1211
    long result;
1212
1213
    /* make sure we have at least one argument */
1214
    if (argc < 1)
1215
        err_throw(VMERR_WRONG_NUM_OF_ARGS);
1216
1217
    /* get the information type code */
1218
    info = pop_int_val(vmg0_);
1219
1220
    /* see what we have */
1221
    switch(info)
1222
    {
1223
    case SYSINFO_SYSINFO:
1224
        /* there are no additional arguments for this information type */
1225
        check_argc(vmg_ argc, 1);
1226
1227
        /* system information is supported in this version - return true */
1228
        retval_true(vmg0_);
1229
        break;
1230
1231
    case SYSINFO_VERSION:
1232
        /* there are no additional arguments for this information type */
1233
        check_argc(vmg_ argc, 1);
1234
1235
        /* return the VM version string, formatted as a string */
1236
        {
1237
            char buf[30];
1238
1239
            sprintf(buf, "%d.%d.%d",
1240
                    (int)((T3VM_VSN_NUMBER >> 16) & 0xffff),
1241
                    (int)((T3VM_VSN_NUMBER >> 8) & 0xff),
1242
                    (int)(T3VM_VSN_NUMBER & 0xff));
1243
            retval_str(vmg_ buf);
1244
        }
1245
        break;
1246
1247
    case SYSINFO_OS_NAME:
1248
        /* there are no additional arguments for this information type */
1249
        check_argc(vmg_ argc, 1);
1250
1251
        /* return the OS name as a string */
1252
        retval_str(vmg_ OS_SYSTEM_NAME);
1253
        break;
1254
1255
    case SYSINFO_HTML:
1256
    case SYSINFO_JPEG:
1257
    case SYSINFO_PNG:
1258
    case SYSINFO_WAV:
1259
    case SYSINFO_MIDI:
1260
    case SYSINFO_WAV_MIDI_OVL:
1261
    case SYSINFO_WAV_OVL:
1262
    case SYSINFO_PREF_IMAGES:
1263
    case SYSINFO_PREF_SOUNDS:
1264
    case SYSINFO_PREF_MUSIC:
1265
    case SYSINFO_PREF_LINKS:
1266
    case SYSINFO_MPEG:
1267
    case SYSINFO_MPEG1:
1268
    case SYSINFO_MPEG2:
1269
    case SYSINFO_MPEG3:
1270
    case SYSINFO_LINKS_HTTP:
1271
    case SYSINFO_LINKS_FTP:
1272
    case SYSINFO_LINKS_NEWS:
1273
    case SYSINFO_LINKS_MAILTO:
1274
    case SYSINFO_LINKS_TELNET:
1275
    case SYSINFO_PNG_TRANS:
1276
    case SYSINFO_PNG_ALPHA:
1277
    case SYSINFO_OGG:
1278
    case SYSINFO_MNG:
1279
    case SYSINFO_MNG_TRANS:
1280
    case SYSINFO_MNG_ALPHA:
1281
    case SYSINFO_TEXT_COLORS:
1282
    case SYSINFO_TEXT_HILITE:
1283
    case SYSINFO_BANNERS:
1284
    case SYSINFO_INTERP_CLASS:
1285
        /* there are no additional arguments for these information types */
1286
        check_argc(vmg_ argc, 1);
1287
1288
        /* ask the OS layer for the information */
1289
        if (os_get_sysinfo(info, 0, &result))
1290
        {
1291
            /* we got a valid result - return it */
1292
            retval_int(vmg_ result);
1293
        }
1294
        else
1295
        {
1296
            /* 
1297
             *   the information type is not known to the OS layer -
1298
             *   return nil to indicate that the information isn't
1299
             *   available 
1300
             */
1301
            retval_nil(vmg0_);
1302
        }
1303
        break;
1304
1305
    case SYSINFO_HTML_MODE:
1306
        /*
1307
         *   This sysinfo flag is explicitly not used in TADS 3, since we're
1308
         *   always in HTML mode.  (We make this case explicit to call
1309
         *   attention to the fact that it was not accidentally omitted, but
1310
         *   is intentionally not used.)  
1311
         */
1312
        /* fall through to default case */
1313
        
1314
    default:
1315
        /*
1316
         *   Other codes fail harmlessly with a nil return value.  Pop all
1317
         *   remaining arguments and return nil.  (Note that we discard
1318
         *   only (argc-1) arguments because we've already popped the
1319
         *   first argument.)  
1320
         */
1321
        G_stk->discard(argc - 1);
1322
        retval_nil(vmg0_);
1323
        break;
1324
    }
1325
}
1326
1327
/* ------------------------------------------------------------------------ */
1328
/*
1329
 *   status_mode - set the status line mode
1330
 */
1331
void CVmBifTIO::status_mode(VMG_ uint argc)
1332
{
1333
    int mode;
1334
    
1335
    /* check arguments */
1336
    check_argc(vmg_ argc, 1);
1337
1338
    /* pop the mode value */
1339
    mode = pop_int_val(vmg0_);
1340
1341
    /* set the new status mode in the console */
1342
    G_console->set_statusline_mode(vmg_ mode);
1343
}
1344
1345
/* ------------------------------------------------------------------------ */
1346
/*
1347
 *   status_right - set the string in the right half of the status line 
1348
 */
1349
void CVmBifTIO::status_right(VMG_ uint argc)
1350
{
1351
    char msg[256];
1352
1353
    /* check arguments */
1354
    check_argc(vmg_ argc, 1);
1355
1356
    /* pop the status string */
1357
    pop_str_val_ui(vmg_ msg, sizeof(msg));
1358
1359
    /* set the string */
1360
    os_strsc(msg);
1361
}
1362
1363
/* ------------------------------------------------------------------------ */
1364
/*
1365
 *   res_exists - check to see if an external resource can be loaded
1366
 *   through the host application 
1367
 */
1368
void CVmBifTIO::res_exists(VMG_ uint argc)
1369
{
1370
    const char *res_name;
1371
    int result;
1372
1373
    /* check arguments */
1374
    check_argc(vmg_ argc, 1);
1375
1376
    /* pop the resource name */
1377
    res_name = pop_str_val(vmg0_);
1378
1379
    /* ask the host application if the resource can be loaded */
1380
    result = G_host_ifc->resfile_exists(res_name + VMB_LEN, osrp2(res_name));
1381
1382
    /* return the result */
1383
    retval_bool(vmg_ result);
1384
}
1385
1386
/* ------------------------------------------------------------------------ */
1387
/*
1388
 *   set_script_file flags 
1389
 */
1390
1391
/* read in 'quiet' mode - do not dipslay output while reading the script */
1392
#define VMBIFTADS_SCRIPT_QUIET     0x0001
1393
1394
/* turn off 'more' mode while reading the script */
1395
#define VMBIFTADS_SCRIPT_NONSTOP   0x0002
1396
1397
/* the script is an "event" script - this is a query-only flag */
1398
#define VMBIFTADS_SCRIPT_EVENT     0x0004
1399
1400
/*
1401
 *   set_script_file special request codes 
1402
 */
1403
#define VMBIFTADS_SCRIPTREQ_GET_STATUS  0x7000
1404
1405
1406
/*
1407
 *   set_script_file - open a command input scripting file 
1408
 */
1409
void CVmBifTIO::set_script_file(VMG_ uint argc)
1410
{
1411
    char fname[OSFNMAX];
1412
    int flags;
1413
1414
    /* check arguments */
1415
    check_argc_range(vmg_ argc, 1, 2);
1416
1417
    /* 
1418
     *   If the filename is nil, close the current script file.  If the
1419
     *   "filename" is a number, it's a special request.  Otherwise, the
1420
     *   filename must be a string giving the name of the file to open. 
1421
     */
1422
    if (G_stk->get(0)->typ == VM_NIL)
1423
    {
1424
        /* discard the nil filename */
1425
        G_stk->discard();
1426
        
1427
        /* pop the flags if present - they're superfluous in this case */
1428
        if (argc >= 2)
1429
            G_stk->discard();
1430
1431
        /* close the script file */
1432
        close_script_file(vmg0_);
1433
    }
1434
    else if (G_stk->get(0)->typ == VM_INT)
1435
    {
1436
        /* get the request code */
1437
        flags = pop_int_val(vmg0_);
1438
        
1439
        /* any additional argument is superfluous in this case */
1440
        if (argc >= 2)
1441
            G_stk->discard();
1442
1443
        /* check the request */
1444
        switch (flags)
1445
        {
1446
        case VMBIFTADS_SCRIPTREQ_GET_STATUS:
1447
            /* get the current script reading status */
1448
            if (G_console->is_reading_script())
1449
            {
1450
                /* a script is in progress - return the script flags */
1451
                retval_int(vmg_
1452
                           (G_console->is_quiet_script()
1453
                            ? VMBIFTADS_SCRIPT_QUIET : 0)
1454
                           | (G_console->is_moremode_script()
1455
                              ? 0 : VMBIFTADS_SCRIPT_NONSTOP)
1456
                           | (G_console->is_event_script()
1457
                              ? VMBIFTADS_SCRIPT_EVENT : 0));
1458
            }
1459
            else
1460
            {
1461
                /* not reading a script - return nil */
1462
                retval_nil(vmg0_);
1463
            }
1464
            break;
1465
        }
1466
    }
1467
    else
1468
    {
1469
        /* 
1470
         *   get the filename string (converted to the file system
1471
         *   character set) 
1472
         */
1473
        pop_str_val_fname(vmg_ fname, sizeof(fname));
1474
    
1475
        /* if they provided flags, pop the flags */
1476
        flags = 0;
1477
        if (argc >= 2)
1478
            flags = pop_int_val(vmg0_);
1479
1480
        /* open the script file */
1481
        G_console->open_script_file(fname,
1482
                                    (flags & VMBIFTADS_SCRIPT_QUIET) != 0,
1483
                                    !(flags & VMBIFTADS_SCRIPT_NONSTOP));
1484
    }
1485
}
1486
1487
/*
1488
 *   close the script file 
1489
 */
1490
void CVmBifTIO::close_script_file(VMG0_)
1491
{
1492
    int old_more_mode;
1493
1494
    /* close the script */
1495
    old_more_mode = G_console->close_script_file();
1496
1497
    /* restore the MORE mode in effect when the script was opened */
1498
    G_console->set_more_state(old_more_mode);
1499
}
1500
1501
/* ------------------------------------------------------------------------ */
1502
/*
1503
 *   get_charset selectors 
1504
 */
1505
1506
/* display character set */
1507
#define VMBIFTADS_CHARSET_DISPLAY  0x0001
1508
1509
/* file system character set for filenames */
1510
#define VMBIFTADS_CHARSET_FILENAME 0x0002
1511
1512
/* typical character set for text file contents */
1513
#define VMBIFTADS_CHARSET_FILECONTENTS 0x0003
1514
1515
/*
1516
 *   get_charset - get a local character set name 
1517
 */
1518
void CVmBifTIO::get_charset(VMG_ uint argc)
1519
{
1520
    char csname[32];
1521
    int which;
1522
1523
    /* check arguments */
1524
    check_argc(vmg_ argc, 1);
1525
1526
    /* get the character set selector */
1527
    which = pop_int_val(vmg0_);
1528
1529
    /* map the selector to the appropriate value */
1530
    switch(which)
1531
    {
1532
    case VMBIFTADS_CHARSET_DISPLAY:
1533
        /* 
1534
         *   if there was an explicit character set parameter specified at
1535
         *   start-up time, use that 
1536
         */
1537
        if (G_disp_cset_name != 0)
1538
        {
1539
            /* there's an explicit parameter - return it */
1540
            retval_str(vmg_ G_disp_cset_name);
1541
1542
            /* we're done */
1543
            return;
1544
        }
1545
1546
        /* no explicit setting - use the OS default character set */
1547
        which = OS_CHARMAP_DISPLAY;
1548
        break;
1549
1550
    case VMBIFTADS_CHARSET_FILENAME:
1551
        which = OS_CHARMAP_FILENAME;
1552
        break;
1553
1554
    case VMBIFTADS_CHARSET_FILECONTENTS:
1555
        which = OS_CHARMAP_FILECONTENTS;
1556
        break;
1557
1558
    default:
1559
        /* others are unrecognized; simply return nil for these */
1560
        retval_nil(vmg0_);
1561
        return;
1562
    }
1563
1564
    /* get the character set */
1565
    os_get_charmap(csname, which);
1566
1567
    /* return a string with the name */
1568
    retval_str(vmg_ csname);
1569
}
1570
1571
/* ------------------------------------------------------------------------ */
1572
/*
1573
 *   flush_output - flush the display output
1574
 */
1575
void CVmBifTIO::flush_output(VMG_ uint argc)
1576
{
1577
    /* we take no arguments */
1578
    check_argc(vmg_ argc, 0);
1579
1580
    /* flush output */
1581
    G_console->flush(vmg_ VM_NL_NONE);
1582
1583
    /* immediately update the display */
1584
    G_console->update_display(vmg0_);
1585
}
1586
1587
/* ------------------------------------------------------------------------ */
1588
/*
1589
 *   input_timeout - get a line of input from the keyboard, with an optional
1590
 *   timeout 
1591
 */
1592
void CVmBifTIO::input_timeout(VMG_ uint argc)
1593
{
1594
    char buf[256];
1595
    long timeout;
1596
    int use_timeout;
1597
    int evt;
1598
    int ele_count;
1599
    vm_obj_id_t lst_obj;
1600
    CVmObjList *lst;
1601
    vm_val_t val;
1602
1603
    /* check arguments */
1604
    check_argc_range(vmg_ argc, 0, 1);
1605
1606
    /* if there's a timeout argument, retrieve it */
1607
    if (argc == 0)
1608
    {
1609
        /* no arguments - there's no timeout */
1610
        use_timeout = FALSE;
1611
        timeout = 0;
1612
    }
1613
    else if (G_stk->get(0)->typ == VM_NIL)
1614
    {
1615
        /* 
1616
         *   there's a timeout argument, but it's nil, so this means there's
1617
         *   no timeout
1618
         */
1619
        use_timeout = FALSE;
1620
        timeout = 0;
1621
1622
        /* discard the argument */
1623
        G_stk->discard();
1624
    }
1625
    else
1626
    {
1627
        /* we have a timeout specified */
1628
        use_timeout = TRUE;
1629
        timeout = pop_long_val(vmg0_);
1630
    }
1631
1632
    /* read a line of text from the keyboard */
1633
    evt = G_console->read_line_timeout(vmg_ buf, sizeof(buf),
1634
                                       timeout, use_timeout);
1635
1636
    /* figure out how big a list we'll return */
1637
    switch(evt)
1638
    {
1639
    case OS_EVT_LINE:
1640
    case OS_EVT_TIMEOUT:
1641
        /* two elements - the event type, and the line of text */
1642
        ele_count = 2;
1643
        break;
1644
1645
    default:
1646
        /* for anything else, we need only the event type code */
1647
        ele_count = 1;
1648
        break;
1649
    }
1650
1651
    /* create the return list */
1652
    lst_obj = CVmObjList::create(vmg_ FALSE, ele_count);
1653
    lst = (CVmObjList *)vm_objp(vmg_ lst_obj);
1654
1655
    /* save the list on the stack to protect against garbage collection */
1656
    val.set_obj(lst_obj);
1657
    G_stk->push(&val);
1658
1659
    /* fill in the first element with the event type code */
1660
    val.set_int(evt);
1661
    lst->cons_set_element(0, &val);
1662
1663
    /* set additional elements, according to the event type */
1664
    switch(evt)
1665
    {
1666
    case OS_EVT_LINE:
1667
    case OS_EVT_TIMEOUT:
1668
        /* the second element is the line of text we read */
1669
        val.set_obj(CVmObjString::create(vmg_ FALSE, buf, strlen(buf)));
1670
        lst->cons_set_element(1, &val);
1671
        break;
1672
1673
    default:
1674
        /* other event types have no extra data */
1675
        break;
1676
    }
1677
1678
    /* return the list */
1679
    retval_obj(vmg_ lst_obj);
1680
1681
    /* we can drop the garbage collection protection now */
1682
    G_stk->discard();
1683
}
1684
1685
/* ------------------------------------------------------------------------ */
1686
/*
1687
 *   input_cancel - cancel input previously interrupted by timeout
1688
 */
1689
void CVmBifTIO::input_cancel(VMG_ uint argc)
1690
{
1691
    vm_val_t val;
1692
    int reset;
1693
    
1694
    /* check arguments */
1695
    check_argc(vmg_ argc, 1);
1696
1697
    /* get the 'reset' flag */
1698
    G_stk->pop(&val);
1699
    reset = (val.typ == VM_TRUE);
1700
1701
    /* cancel the line */
1702
    G_console->read_line_cancel(vmg_ reset);
1703
}
1704
1705
/* ------------------------------------------------------------------------ */
1706
/*
1707
 *   Banner Window Functions 
1708
 */
1709
1710
/*
1711
 *   create a banner 
1712
 */
1713
void CVmBifTIO::banner_create(VMG_ uint argc)
1714
{
1715
    int parent_id;
1716
    int other_id;
1717
    int where;
1718
    int wintype;
1719
    int align;
1720
    int siz;
1721
    int siz_units;
1722
    unsigned long style;
1723
    int hdl;
1724
1725
    /* check arguments */
1726
    check_argc_range(vmg_ argc, 7, 8);
1727
1728
    /* retrieve the 'parent' parameter */
1729
    if (argc == 7)
1730
    {
1731
        /* 
1732
         *   there's no parent argument - this is an obsolete format for the
1733
         *   arguments, but accept it for now, and simply treat it as
1734
         *   equivalent to a nil parent 
1735
         */
1736
        parent_id = 0;
1737
    }
1738
    else if (G_stk->get(0)->typ == VM_NIL)
1739
    {
1740
        /* parent is nil - use zero as the ID and discard the nil */
1741
        parent_id = 0;
1742
        G_stk->discard();
1743
    }
1744
    else
1745
    {
1746
        /* retrieve the parent ID */
1747
        parent_id = pop_int_val(vmg0_);
1748
    }
1749
1750
    /* retrieve the 'where' parameter */
1751
    where = pop_int_val(vmg0_);
1752
1753
    /* retrieve the 'other' parameter, if it's needed for the 'where' */
1754
    switch(where)
1755
    {
1756
    case OS_BANNER_BEFORE:
1757
    case OS_BANNER_AFTER:
1758
        /* we need another banner ID for the relative insertion point */
1759
        other_id = pop_int_val(vmg0_);
1760
        break;
1761
1762
    default:
1763
        /* we don't need 'other' for this insertion point */
1764
        other_id = 0;
1765
        G_stk->discard();
1766
        break;
1767
    }
1768
1769
    /* retrieve the window type argument */
1770
    wintype = pop_int_val(vmg0_);
1771
1772
    /* retrieve the alignment argument */
1773
    align = pop_int_val(vmg0_);
1774
1775
    /* retrieve the size (as a percentage of the full screen size) */
1776
    if (G_stk->get(0)->typ == VM_NIL)
1777
    {
1778
        /* nil size - use zero for the size */
1779
        siz = 0;
1780
        siz_units = OS_BANNER_SIZE_ABS;
1781
1782
        /* discard the size and size units */
1783
        G_stk->discard();
1784
        G_stk->discard();
1785
    }
1786
    else
1787
    {
1788
        /* retrieve the size and size units as integer values */
1789
        siz = pop_int_val(vmg0_);
1790
        siz_units = pop_int_val(vmg0_);
1791
    }
1792
1793
    /* retrieve the flags */
1794
    style = pop_long_val(vmg0_);
1795
1796
    /* try creating the banner */
1797
    hdl = G_console->get_banner_manager()->create_banner(
1798
        vmg_ parent_id, where, other_id, wintype,
1799
        align, siz, siz_units, style);
1800
1801
    /* 
1802
     *   If we succeeded, return the handle; otherwise, return nil.  A banner
1803
     *   handle of zero indicates failure. 
1804
     */
1805
    if (hdl != 0)
1806
        retval_int(vmg_ hdl);
1807
    else
1808
        retval_nil(vmg0_);
1809
}
1810
1811
/*
1812
 *   delete a banner 
1813
 */
1814
void CVmBifTIO::banner_delete(VMG_ uint argc)
1815
{
1816
    /* check arguments */
1817
    check_argc(vmg_ argc, 1);
1818
1819
    /* delete the banner */
1820
    G_console->get_banner_manager()->delete_banner(pop_int_val(vmg0_));
1821
}
1822
1823
/*
1824
 *   clear a window 
1825
 */
1826
void CVmBifTIO::banner_clear(VMG_ uint argc)
1827
{
1828
    int id;
1829
    CVmConsole *console;
1830
1831
    /* check arguments */
1832
    check_argc(vmg_ argc, 1);
1833
1834
    /* get the banner ID */
1835
    id = pop_int_val(vmg0_);
1836
1837
    /* get the banner - if it's invalid, throw an error */
1838
    console = G_console->get_banner_manager()->get_console(id);
1839
    if (console == 0)
1840
        err_throw(VMERR_BAD_VAL_BIF);
1841
1842
    /* tell the console that we're clearing it */
1843
    console->clear_window(vmg0_);
1844
}
1845
1846
/*
1847
 *   write values to a banner 
1848
 */
1849
void CVmBifTIO::banner_say(VMG_ uint argc)
1850
{
1851
    CVmConsole *console;
1852
1853
    /* check arguments */
1854
    check_argc_range(vmg_ argc, 1, 32767);
1855
1856
    /* get the banner - if it's invalid, throw an error */
1857
    console =
1858
        G_console->get_banner_manager()->get_console(pop_int_val(vmg0_));
1859
    if (console == 0)
1860
        err_throw(VMERR_BAD_VAL_BIF);
1861
1862
    /* 
1863
     *   write the argument(s) to the console (note that the first argument,
1864
     *   which we've already retrieved, is the console handle, so don't count
1865
     *   it among the arguments to display) 
1866
     */
1867
    say_to_console(vmg_ console, argc - 1);
1868
}
1869
1870
/*
1871
 *   flush text to a banner 
1872
 */
1873
void CVmBifTIO::banner_flush(VMG_ uint argc)
1874
{
1875
    CVmConsole *console;
1876
1877
    /* check arguments */
1878
    check_argc(vmg_ argc, 1);
1879
1880
    /* get the banner - if it's invalid, throw an error */
1881
    console =
1882
        G_console->get_banner_manager()->get_console(pop_int_val(vmg0_));
1883
    if (console == 0)
1884
        err_throw(VMERR_BAD_VAL_BIF);
1885
1886
    /* flush the console */
1887
    console->flush(vmg_ VM_NL_NONE);
1888
1889
    /* immediately update the display */
1890
    console->update_display(vmg0_);
1891
}
1892
1893
/*
1894
 *   set the banner size 
1895
 */
1896
void CVmBifTIO::banner_set_size(VMG_ uint argc)
1897
{
1898
    int id;
1899
    void *hdl;
1900
    int siz;
1901
    int siz_units;
1902
    int is_advisory;
1903
1904
    /* check arguments */
1905
    check_argc(vmg_ argc, 4);
1906
1907
    /* get the banner ID */
1908
    id = pop_int_val(vmg0_);
1909
1910
    /* get the size and size units */
1911
    siz = pop_int_val(vmg0_);
1912
    siz_units = pop_int_val(vmg0_);
1913
1914
    /* get the is-advisory flag */
1915
    is_advisory = pop_bool_val(vmg0_);
1916
1917
    /* get the banner - if it's invalid, throw an error */
1918
    hdl = G_console->get_banner_manager()->get_os_handle(id);
1919
    if (hdl == 0)
1920
        err_throw(VMERR_BAD_VAL_BIF);
1921
1922
    /* set the size */
1923
    os_banner_set_size(hdl, siz, siz_units, is_advisory);
1924
}
1925
1926
/*
1927
 *   size a banner to its contents in one or both dimensions 
1928
 */
1929
void CVmBifTIO::banner_size_to_contents(VMG_ uint argc)
1930
{
1931
    int id;
1932
    void *hdl;
1933
    CVmConsole *console;
1934
1935
    /* check arguments */
1936
    check_argc(vmg_ argc, 1);
1937
1938
    /* get the banner ID */
1939
    id = pop_int_val(vmg0_);
1940
1941
    /* get the banner - if it's invalid, throw an error */
1942
    hdl = G_console->get_banner_manager()->get_os_handle(id);
1943
    console = G_console->get_banner_manager()->get_console(id);
1944
    if (hdl == 0 || console == 0)
1945
        err_throw(VMERR_BAD_VAL_BIF);
1946
1947
    /* make sure we've flushed any pending output to the banner */
1948
    console->flush(vmg_ VM_NL_NONE);
1949
1950
    /* set the size */
1951
    os_banner_size_to_contents(hdl);
1952
}
1953
1954
/*
1955
 *   move the output position in a text grid banner
1956
 */
1957
void CVmBifTIO::banner_goto(VMG_ uint argc)
1958
{
1959
    int id;
1960
    void *hdl;
1961
    CVmConsole *console;
1962
    int row, col;
1963
1964
    /* check arguments */
1965
    check_argc(vmg_ argc, 3);
1966
1967
    /* get the banner ID */
1968
    id = pop_int_val(vmg0_);
1969
1970
    /* get the coordinates */
1971
    row = pop_int_val(vmg0_);
1972
    col = pop_int_val(vmg0_);
1973
1974
    /* make sure the values are valid */
1975
    if (row < 1 || col < 1)
1976
        err_throw(VMERR_BAD_VAL_BIF);
1977
1978
    /* get the banner - if it's invalid, throw an error */
1979
    hdl = G_console->get_banner_manager()->get_os_handle(id);
1980
    console = G_console->get_banner_manager()->get_console(id);
1981
    if (hdl == 0 || console == 0)
1982
        err_throw(VMERR_BAD_VAL_BIF);
1983
1984
    /* 
1985
     *   make sure we've flushed and discarded any pending output (since we
1986
     *   don't want any pending output to show up at the new cursor position)
1987
     */
1988
    console->flush(vmg_ VM_NL_NONE);
1989
    console->empty_buffers(vmg0_);
1990
1991
    /* move the cursor, adjusting from 1-based to 0-based coordinates */
1992
    os_banner_goto(hdl, row - 1, col - 1);
1993
}
1994
1995
/*
1996
 *   set the text color in a banner 
1997
 */
1998
void CVmBifTIO::banner_set_text_color(VMG_ uint argc)
1999
{
2000
    int id;
2001
    void *hdl;
2002
    CVmConsole *console;
2003
    os_color_t fg, bg;
2004
2005
    /* check arguments */
2006
    check_argc(vmg_ argc, 3);
2007
2008
    /* get the banner ID */
2009
    id = pop_int_val(vmg0_);
2010
2011
    /* get the foreground and background color values */
2012
    fg = (os_color_t)pop_long_val(vmg0_);
2013
    bg = (os_color_t)pop_long_val(vmg0_);
2014
2015
    /* get the banner - if it's invalid, throw an error */
2016
    hdl = G_console->get_banner_manager()->get_os_handle(id);
2017
    console = G_console->get_banner_manager()->get_console(id);
2018
    if (hdl == 0 || console == 0)
2019
        err_throw(VMERR_BAD_VAL_BIF);
2020
2021
    /* set the text output color in the console's formatter */
2022
    console->set_text_color(vmg_ fg, bg);
2023
}
2024
2025
/*
2026
 *   set the screen color in a banner 
2027
 */
2028
void CVmBifTIO::banner_set_screen_color(VMG_ uint argc)
2029
{
2030
    int id;
2031
    void *hdl;
2032
    CVmConsole *console;
2033
    os_color_t color;
2034
2035
    /* check arguments */
2036
    check_argc(vmg_ argc, 2);
2037
2038
    /* get the banner ID */
2039
    id = pop_int_val(vmg0_);
2040
2041
    /* get the body color */
2042
    color = (os_color_t)pop_long_val(vmg0_);
2043
2044
    /* get the banner - if it's invalid, throw an error */
2045
    hdl = G_console->get_banner_manager()->get_os_handle(id);
2046
    console = G_console->get_banner_manager()->get_console(id);
2047
    if (hdl == 0 || console == 0)
2048
        err_throw(VMERR_BAD_VAL_BIF);
2049
2050
    /* set the body color in the console */
2051
    console->set_body_color(vmg_ color);
2052
}
2053
2054
/* service routine: store an integer in a list under construction */
2055
static void set_list_int(CVmObjList *lst, size_t idx, long intval)
2056
{
2057
    vm_val_t val;
2058
    
2059
    /* set the value */
2060
    val.set_int(intval);
2061
2062
    /* store it in the list */
2063
    lst->cons_set_element(idx, &val);
2064
}
2065
2066
/*
2067
 *   get information on a banner
2068
 */
2069
void CVmBifTIO::banner_get_info(VMG_ uint argc)
2070
{
2071
    int id;
2072
    void *hdl;
2073
    os_banner_info_t info;
2074
    CVmConsoleBanner *console;
2075
2076
    /* check arguments */
2077
    check_argc(vmg_ argc, 1);
2078
2079
    /* get the banner ID */
2080
    id = pop_int_val(vmg0_);
2081
2082
    /* get the banner - if it's invalid, throw an error */
2083
    hdl = G_console->get_banner_manager()->get_os_handle(id);
2084
    console = G_console->get_banner_manager()->get_console(id);
2085
    if (hdl == 0 || console == 0)
2086
        err_throw(VMERR_BAD_VAL_BIF);
2087
2088
    /* get information on the banner */
2089
    if (console->get_banner_info(&info))
2090
    {
2091
        vm_obj_id_t lst_obj;
2092
        CVmObjList *lst;
2093
        vm_val_t val;
2094
        
2095
        /* set up a return list with space for six entries */
2096
        lst_obj = CVmObjList::create(vmg_ FALSE, 6);
2097
        lst = (CVmObjList *)vm_objp(vmg_ lst_obj);
2098
2099
        /* save the list on the stack to protect against garbage collection */
2100
        val.set_obj(lst_obj);
2101
        G_stk->push(&val);
2102
2103
        /* 
2104
         *   return the values: [align, style, rows, columns, pix_height,
2105
         *   pix_width] 
2106
         */
2107
        set_list_int(lst, 0, info.align);
2108
        set_list_int(lst, 1, info.style);
2109
        set_list_int(lst, 2, info.rows);
2110
        set_list_int(lst, 3, info.columns);
2111
        set_list_int(lst, 4, info.pix_height);
2112
        set_list_int(lst, 5, info.pix_width);
2113
2114
        /* return the list */
2115
        retval_obj(vmg_ lst_obj);
2116
2117
        /* discard our gc protection */
2118
        G_stk->discard();
2119
    }
2120
    else
2121
    {
2122
        /* no information available - return nil */
2123
        retval_nil(vmg0_);
2124
    }
2125
}
2126
2127
/* ------------------------------------------------------------------------ */
2128
/*
2129
 *   Log Console Functions
2130
 */
2131
2132
/*
2133
 *   create a log console 
2134
 */
2135
void CVmBifTIO::log_console_create(VMG_ uint argc)
2136
{
2137
    char fname[OSFNMAX];
2138
    osfildef *fp;
2139
    CCharmapToLocal *cmap;
2140
    int width;
2141
    int hdl;
2142
    
2143
    /* check arguments */
2144
    check_argc(vmg_ argc, 3);
2145
2146
    /* retrieve the log file name */
2147
    pop_str_val_fname(vmg_ fname, sizeof(fname));
2148
2149
    /* 
2150
     *   Retrieve the character mapper, which can be given as either a
2151
     *   CharacterSet object or a string giving the character set name.  
2152
     */
2153
    if (G_stk->get(0)->typ == VM_NIL)
2154
    {
2155
        /* nil - use the default log file character set */
2156
        cmap = G_cmap_to_log;
2157
2158
        /* add our reference to it */
2159
        cmap->add_ref();
2160
    }
2161
    else if (G_stk->get(0)->typ == VM_OBJ
2162
             && CVmObjCharSet::is_charset(vmg_ G_stk->get(0)->val.obj))
2163
    {
2164
        vm_obj_id_t obj;
2165
        
2166
        /* it's a CharacterSet object - pop the reference */
2167
        obj = CVmBif::pop_obj_val(vmg0_);
2168
2169
        /* retrieve the character mapper from the character set */
2170
        cmap = ((CVmObjCharSet *)vm_objp(vmg_ obj))->get_to_local(vmg0_);
2171
2172
        /* add our reference to it */
2173
        cmap->add_ref();
2174
    }
2175
    else
2176
    {
2177
        const char *str;
2178
        size_t len;
2179
        char *nm;
2180
2181
        /* it's not a CharacterSet, so it must be a character set name */
2182
        str = G_stk->get(0)->get_as_string(vmg0_);
2183
        if (str == 0)
2184
            err_throw(VMERR_BAD_TYPE_BIF);
2185
        
2186
        /* get the length and skip the length prefix */
2187
        len = vmb_get_len(str);
2188
        str += VMB_LEN;
2189
2190
        /* get a null-terminated version of the name */
2191
        nm = lib_copy_str(str, len);
2192
2193
        /* 
2194
         *   Create a character mapping for the given name.  Note that this
2195
         *   will automatically add a reference to the mapper on our behalf,
2196
         *   so we don't have to add our own extra reference. 
2197
         */
2198
        cmap = CCharmapToLocal::load(G_host_ifc->get_cmap_res_loader(), nm);
2199
2200
        /* done with the null-terminated version of the name string */
2201
        lib_free_str(nm);
2202
2203
        /* discard the argument */
2204
        G_stk->discard();
2205
    }
2206
2207
    /* if we didn't get a character map, use us-ascii by default */
2208
    if (cmap == 0)
2209
        cmap = CCharmapToLocal::load(G_host_ifc->get_cmap_res_loader(),
2210
                                     "us-ascii");
2211
2212
    err_try
2213
    {
2214
        /* make sure the file safety level allows the operation */
2215
        CVmObjFile::check_safety_for_open(vmg_ fname, VMOBJFILE_ACCESS_WRITE);
2216
        
2217
        /* open the file for writing (in text mode) */
2218
        fp = osfopwt(fname, OSFTLOG);
2219
    
2220
        /* if that failed, we can't contine */
2221
        if (fp == 0)
2222
            G_interpreter->throw_new_class(vmg_ G_predef->file_creation_exc,
2223
                                           0, "error creating log file");
2224
2225
        /* retrieve the width */
2226
        width = pop_int_val(vmg0_);
2227
2228
        /* create the log console */
2229
        hdl = G_console->get_log_console_manager()->create_log_console(
2230
            fname, fp, cmap, width);
2231
    }
2232
    err_finally
2233
    {
2234
        /* 
2235
         *   release our character map reference - if we succeeded in
2236
         *   creating the log console, it will have added its own reference
2237
         *   by now 
2238
         */
2239
        cmap->release_ref();
2240
    }
2241
    err_end;
2242
2243
    /* 
2244
     *   If we succeeded, return the handle; otherwise, return nil.  A handle
2245
     *   of zero indicates failure.  
2246
     */
2247
    if (hdl != 0)
2248
        retval_int(vmg_ hdl);
2249
    else
2250
        retval_nil(vmg0_);
2251
}
2252
2253
/*
2254
 *   close (delete) a log console
2255
 */
2256
void CVmBifTIO::log_console_close(VMG_ uint argc)
2257
{
2258
    int handle;
2259
    CVmConsole *console;
2260
    
2261
    /* check arguments */
2262
    check_argc(vmg_ argc, 1);
2263
2264
    /* get the handle */
2265
    handle = pop_int_val(vmg0_);
2266
2267
    /* get the console based on the handle */
2268
    console = G_console->get_log_console_manager()->get_console(handle);
2269
    if (console == 0)
2270
        err_throw(VMERR_BAD_VAL_BIF);
2271
2272
    /* flush the console */
2273
    console->flush(vmg_ VM_NL_NONE);
2274
2275
    /* delete the console */
2276
    G_console->get_log_console_manager()->delete_log_console(handle);
2277
}
2278
2279
/*
2280
 *   write values to a log console
2281
 */
2282
void CVmBifTIO::log_console_say(VMG_ uint argc)
2283
{
2284
    int hdl;
2285
    CVmConsole *console;
2286
    CVmConsoleMainLog main_log_console;
2287
2288
    /* check arguments */
2289
    check_argc_range(vmg_ argc, 1, 32767);
2290
2291
    /* get the console handle */
2292
    hdl = pop_int_val(vmg0_);
2293
2294
    /* 
2295
     *   if it's the special value -1, it means that we want to write to the
2296
     *   main console's log file; otherwise, it's a log console that we
2297
     *   previously created explicitly via log_console_create()
2298
     */
2299
    if (hdl == -1)
2300
    {
2301
        /* use the main log */
2302
        console = &main_log_console;
2303
    }
2304
    else
2305
    {
2306
        /* get the console by handle - if it's invalid, throw an error */
2307
        console = G_console->get_log_console_manager()->get_console(hdl);
2308
        if (console == 0)
2309
            err_throw(VMERR_BAD_VAL_BIF);
2310
    }
2311
2312
    /*
2313
     *   write the argument(s) to the console (note that the first argument,
2314
     *   which we've already retrieved, is the console handle, so don't count
2315
     *   it among the arguments to display) 
2316
     */
2317
    say_to_console(vmg_ console, argc - 1);
2318
}
2319