cfad47cfa3/tads3/vmconsol.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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
  vmconsol.h - TADS 3 console input reader and output formatter
12
Function
13
  Provides console input and output for the TADS 3 built-in function set
14
  for the T3 VM.
15
16
  T3 uses the UTF-8 character set to represent character strings.  The OS
17
  functions use the local character set.  We perform the mapping between
18
  UTF-8 and the local character set within this module, so that OS routines
19
  see local characters only, not UTF-8.
20
21
  This code is based on the TADS 2 output formatter, but has been
22
  substantially reworked for C++, Unicode, and the slightly different
23
  TADS 3 formatting model.
24
Notes
25
  
26
Modified
27
  09/04/99 MJRoberts  - Creation
28
*/
29
30
#ifndef VMCONSOL_H
31
#define VMCONSOL_H
32
33
#include <string.h>
34
#include <stdlib.h>
35
#include "wchar.h"
36
37
#include "os.h"
38
#include "t3std.h"
39
#include "vmglob.h"
40
41
42
/* ------------------------------------------------------------------------ */
43
/*
44
 *   Synthetic console events.  These are events we generate alongside
45
 *   OS_EVT_xxx codes; we start these at 10000 to ensure that we don't
46
 *   overlap any current or future OS_EVT_xxx event codes.  
47
 */
48
#define VMCON_EVT_END_SCRIPT   10000
49
#define VMCON_EVT_DIALOG       10001
50
#define VMCON_EVT_FILE         10002
51
52
/*
53
 *   Event attributes.  Some scriptable events have extra attributes
54
 *   associated with them in addition to the main payload.  These attributes
55
 *   take the syntactic form of HTML tag attributes, so in principal we could
56
 *   have arbitrarily many attributes, each of which have arbitrary string
57
 *   values.  However, at the moment, we only have a small number of
58
 *   attributes, and each attribute's value is merely a boolean, so it's
59
 *   adequate to represent these in the call interface with bit flags.  
60
 */
61
62
/* OVERWRITE, as in <file overwrite> */
63
#define VMCON_EVTATTR_OVERWRITE  0x00000001
64
65
66
67
/* ------------------------------------------------------------------------ */
68
/*
69
 *   Newline type codes.  These specify how we are to perform line
70
 *   breaking after writing out text.
71
 */
72
enum vm_nl_type
73
{
74
    /* 
75
     *   no line separation at all - write out this text and subsequent
76
     *   text as part of the same line with no separators 
77
     */
78
    VM_NL_NONE,
79
80
    /* 
81
     *   flushing in preparation for input - don't show any line separation,
82
     *   and make sure that we display everything in the buffer, including
83
     *   trailing spaces 
84
     */
85
    VM_NL_INPUT,
86
87
    /* break the line at the end of this text and start a newline */
88
    VM_NL_NEWLINE,
89
90
    /* OS line separation - add a space after the text */
91
    VM_NL_OSNEWLINE,
92
93
    /* 
94
     *   flushing internal buffers only: no line separation, and do not
95
     *   flush to underlying OS level yet 
96
     */
97
    VM_NL_NONE_INTERNAL
98
};
99
100
101
/* ------------------------------------------------------------------------ */
102
/*
103
 *   Handle manager.  This is a simple class for mapping system objects to
104
 *   integers, which we give as "handles" to byte code callers.  We give only
105
 *   the integer handles to byte code to ensure that handles given back to us
106
 *   by the byte code are valid; if we handed back raw pointers to the byte
107
 *   code, it could call us with random garbage, and we'd have no way to
108
 *   protect against it.  
109
 */
110
class CVmHandleManager
111
{
112
public:
113
    CVmHandleManager();
114
115
    /* 
116
     *   delete the object (use this rather than calling the destructor
117
     *   directly, since we need to call some virtuals in the course of
118
     *   preparing for deletion 
119
     */
120
    void delete_obj();
121
122
protected:
123
    /* delete */
124
    virtual ~CVmHandleManager();
125
126
    /* allocate a slot for a new item */
127
    int alloc_handle(void *item);
128
129
    /* clear the given handle's slot */
130
    void clear_handle(int handle)
131
    {
132
        /* if the handle is valid, clear its associated slot */
133
        if (is_valid_handle(handle))
134
            handles_[handle - 1] = 0;
135
    }
136
137
    /* is the given handle valid? */
138
    int is_valid_handle(int handle)
139
    {
140
        /* 
141
         *   it's valid if it's within range for the allocated slots, and
142
         *   there's a non-null object in the handle's slot 
143
         */
144
        return (handle >= 1
145
                && (size_t)handle <= handles_max_
146
                && handles_[handle - 1] != 0);
147
    }
148
149
    /* get the object for the given handle */
150
    void *get_object(int handle)
151
    {
152
        /* 
153
         *   If the handle is valid, get the item from the slot; the handle
154
         *   is indexed from 1, so decrement it to get the array index of the
155
         *   object for the handle.  If the handle is invalid, return null. 
156
         */
157
        return is_valid_handle(handle) ? handles_[handle - 1] : 0;
158
    }
159
160
    /* 
161
     *   Delete the item in a slot, in preparation for destroying the handle
162
     *   manager itself - each subclass must override this to do the
163
     *   appropriate work on termination.  
164
     */
165
    virtual void delete_handle_object(int handle, void *obj) = 0;
166
167
    /* array of window banners */
168
    void **handles_;
169
    size_t handles_max_;
170
};
171
172
173
/* ------------------------------------------------------------------------ */
174
/*
175
 *   Banner manager.  This keeps track of banner windows outside of the main
176
 *   console window.  
177
 */
178
class CVmBannerManager: public CVmHandleManager
179
{
180
public:
181
    CVmBannerManager() { }
182
183
    /*
184
     *   Create a banner window.  This creates an OS-level banner window, and
185
     *   creates a console object to format its output.  Returns a banner
186
     *   handle that can be used to refer to the window.  Banner handle zero
187
     *   is invalid and indicates failure.
188
     *   
189
     *   'parent_id' is the banner ID of the parent of the new banner.  The
190
     *   new banner is created as a child of the given parent.  If parent_id
191
     *   is zero, then the new banner is created as a child of the main
192
     *   window.  The parent determines how the new window is laid out: the
193
     *   new window's display area is carved out of the display area of the
194
     *   parent.
195
     *   
196
     *   'where' is OS_BANNER_FIRST, OS_BANNER_LAST, OS_BANNER_BEFORE, or
197
     *   OS_BANNER_AFTER.  'other_id' is the banner ID of an existing child
198
     *   of the given parent, for the relative insertion point; this is
199
     *   ignored for OS_BANNER_FIRST and OS_BANNER_LAST.
200
     *   
201
     *   'wintype' is an OS_BANNER_TYPE_xxx code giving the type of window to
202
     *   be created.
203
     *   
204
     *   'siz' is the size, in units specified by 'siz_units', an
205
     *   OS_BANNER_SIZE_xxx value.
206
     *   
207
     *   'style' is a combination of OS_BANNER_STYLE_xxx flags.  
208
     */
209
    int create_banner(VMG_ int parent_id,
210
                      int where, int other_id, int wintype,
211
                      int align, int siz, int siz_units,
212
                      unsigned long style);
213
214
    /* delete a banner */
215
    void delete_banner(int banner)
216
        { delete_or_orphan_banner(banner, FALSE); }
217
218
    /* 
219
     *   Get the OS-level handle for the banner - this handle can be used to
220
     *   call the os_banner_xxx functions directly.  
221
     */
222
    void *get_os_handle(int banner);
223
224
    /* get the banner's console object */
225
    class CVmConsoleBanner *get_console(int banner)
226
    {
227
        /* the object behind the handle is the console */
228
        return (CVmConsoleBanner *)get_object(banner);
229
    }
230
231
    /* flush all banners */
232
    void flush_all(VMG_ vm_nl_type nl);
233
234
protected:
235
    /* delete the object in a slot, in preparation for deleting the manager */
236
    virtual void delete_handle_object(int handle, void *)
237
    {
238
        /* 
239
         *   delete the banner object, but orphan the system-level banner -
240
         *   this will allow the system-level banner to remain visible even
241
         *   after VM termination, in case the host application continues
242
         *   running even after the VM exits 
243
         */
244
        delete_or_orphan_banner(handle, TRUE);
245
    }
246
247
    /* delete or orphan a banner window */
248
    void delete_or_orphan_banner(int banner, int orphan);
249
};
250
251
/* ------------------------------------------------------------------------ */
252
/*
253
 *   Log console manager.  This keeps track of log consoles, which are
254
 *   consoles created specifically for capturing text to a log file.  
255
 */
256
class CVmLogConsoleManager: public CVmHandleManager
257
{
258
public:
259
    CVmLogConsoleManager() { }
260
261
    /* create a log console - returns the console handle */
262
    int create_log_console(const char *fname, osfildef *fp,
263
                           class CCharmapToLocal *cmap, int width);
264
265
    /* delete a log console */
266
    void delete_log_console(int handle);
267
268
    /* get the log's console object */
269
    class CVmConsoleLog *get_console(int banner)
270
    {
271
        /* the object behind the handle is the console */
272
        return (CVmConsoleLog *)get_object(banner);
273
    }
274
275
protected:
276
    /* delete the object associated with a handle */
277
    virtual void delete_handle_object(int handle, void *)
278
    {
279
        /* delete the console */
280
        delete_log_console(handle);
281
    }
282
};
283
284
/*
285
 *   Log console manager item.  We use these to track individual log
286
 *   consoles.  
287
 */
288
class CVmLogConsoleItem
289
{
290
public:
291
    CVmLogConsoleItem(const char *fname, class CCharmapToLocal *cmap);
292
    ~CVmLogConsoleItem();
293
294
    /* get the console */
295
    class CVmConsoleLog *get_console() const { return console_; }
296
297
protected:
298
    /* my console object */
299
    class CVmConsoleLog *console_;
300
};
301
302
303
/* ------------------------------------------------------------------------ */
304
/*
305
 *   Script stack entry 
306
 */
307
struct script_stack_entry
308
{
309
    script_stack_entry(script_stack_entry *encp, int old_more,
310
                       osfildef *outfp,
311
                       int new_more, int is_quiet, int is_event_script)
312
    {
313
        this->enc = encp;
314
        this->old_more_mode = old_more;
315
        this->fp = outfp;
316
        this->more_mode = new_more;
317
        this->quiet = is_quiet;
318
        this->event_script = is_event_script;
319
    }
320
321
    /* the enclosing stack level */
322
    script_stack_entry *enc;
323
324
    /* the script file at this level */
325
    osfildef *fp;
326
327
    /* the MORE mode that was in effect before this script file */
328
    int old_more_mode;
329
330
    /* the MORE mode in effect during this script */
331
    int more_mode;
332
333
    /* are we reading quietly from this script? */
334
    int quiet;
335
336
    /* is this an event script? */
337
    int event_script;
338
};
339
340
341
/* ------------------------------------------------------------------------ */
342
/*
343
 *   Console.  A console corresponds to device that shows information to
344
 *   and reads text from the user.  On a text system, the console is
345
 *   simply the terminal.  On a graphical system, the console is usually
346
 *   an application window.  
347
 */
348
class CVmConsole
349
{
350
public:
351
    CVmConsole();
352
    virtual ~CVmConsole();
353
354
    /* write out a null-terminated UTF-8 string */
355
    int format_text(VMG_ const char *p)
356
    {
357
        /* get its length and write it out */
358
        return format_text(vmg_ p, strlen(p));
359
    }
360
361
    /* write out a UTF-8 string of a given byte length */
362
    virtual int format_text(VMG_ const char *p, size_t len);
363
364
    /* format text explicitly to the log file, if any */
365
    int format_text_to_log(VMG_ const char *p, size_t len);
366
367
    /* display a blank line */
368
    void write_blank_line(VMG0_);
369
370
    /* set the whitespace mode (returns the old whitespace mode) */
371
    int set_obey_whitespace(int f);
372
373
    /* set the text color */
374
    void set_text_color(VMG_ os_color_t fg, os_color_t bg);
375
376
    /* set the body color */
377
    void set_body_color(VMG_ os_color_t color);
378
379
    /* set the caps flag - capitalize the next character */
380
    void caps();
381
382
    /* set the nocaps flag - make the next letter miniscule */
383
    void nocaps();
384
385
    /* flush the output with the given newline type */
386
    void flush(VMG_ vm_nl_type nl);
387
388
    /* flush the output, ending the current line and starting a new line */
389
    void flush(VMG0_) { flush(vmg_ VM_NL_NEWLINE); }
390
391
    /* empty our buffers */
392
    void empty_buffers(VMG0_);
393
394
    /* clear the window */
395
    virtual void clear_window(VMG0_) = 0;
396
397
    /* 
398
     *   Flush all windows we control.  By default, we just flush our own
399
     *   window; consoles that manage multiple windows should flush their
400
     *   managed windows here as well. 
401
     */
402
    virtual void flush_all(VMG_ vm_nl_type nl) { flush(vmg_ nl); }
403
404
    /* immediately update the display window */
405
    void update_display(VMG0_);
406
407
    /* 
408
     *   Open a new log file.  Closes any previous log file.  If the file
409
     *   already exists, we'll overwrite it with the new log information,
410
     *   otherwise we'll create a new file.  Returns zero on success,
411
     *   non-zero on failure.  
412
     */
413
    int open_log_file(const char *fname);
414
415
    /* 
416
     *   close any existing log file - returns zero on success, non-zero
417
     *   on failure 
418
     */
419
    int close_log_file();
420
421
    /*
422
     *   Open a new command log file.  We'll log commands (and only
423
     *   commands) to the command log file.  Returns zero on success,
424
     *   non-zero on failure.  
425
     */
426
    int open_command_log(const char *fname, int event_script);
427
428
    /* close the command log file, if there is one */
429
    int close_command_log();
430
431
    /*
432
     *   Set the current MORE mode.  Returns the old state.  The state is
433
     *   true if we show MORE prompts, false if not.  The state will be
434
     *   false if the underlying OS display layer handles prompting, so a
435
     *   return of false doesn't necessarily mean that MORE prompts are
436
     *   never shown, but merely that we don't handle MORE prompts in the
437
     *   output formatter itself.  
438
     */
439
    virtual int set_more_state(int state) = 0;
440
441
    /* determine if we're in MORE mode */
442
    virtual int is_more_mode() const = 0;
443
444
    /* get the line width of the display device */
445
    virtual int get_line_width() const = 0;
446
447
    /* 
448
     *   Do we allow overrunning the line width when we can't find a natural
449
     *   breaking point (at a whitespace character, for example) such that
450
     *   we can fit some text within the line width?
451
     *   
452
     *   If this returns false, then we'll force a newline when we reach the
453
     *   line width, even if doing so breaks up a single word that doesn't
454
     *   have a natural breaking point within.  
455
     */
456
    virtual int allow_overrun() const = 0;
457
458
    /* get the page length of the display device */
459
    virtual int get_page_length() const = 0;
460
461
    /* get/set the double-space flag (for periods and other punctuation) */
462
    int get_doublespace() const { return doublespace_; }
463
    void set_doublespace(int f) { doublespace_ = f; }
464
465
    /* reset the MORE prompt line count */
466
    void reset_line_count(int clearing);
467
468
    /* 
469
     *   check to see if we're reading from a script input file - returns
470
     *   true if so, false if reading from the user (via the keyboard or
471
     *   other input device) 
472
     */
473
    int is_reading_script() const { return (script_sp_ != 0); }
474
475
    /* 
476
     *   check to see if we're reading quietly from a script - if we're
477
     *   reading from a script, and this flag is true, we suppress all
478
     *   output
479
     */
480
    int is_quiet_script() const
481
        { return (script_sp_ != 0 && script_sp_->quiet); }
482
483
    /* is the script in MORE mode? */
484
    int is_moremode_script() const
485
        { return (script_sp_ != 0 && script_sp_->more_mode); }
486
487
    /* is the script an <eventscript> type? */
488
    int is_event_script() const
489
        { return (script_sp_ != 0 && script_sp_->event_script); }
490
491
    /* 
492
     *   Open a script file.  If 'quiet' is true, no output is displayed
493
     *   while the script is being processed.  If 'script_more_mode' is
494
     *   true, MORE mode is in effect while processing the script,
495
     *   otherwise MORE mode is turned off while processing the script (to
496
     *   leave things as they are, simply pass in is_more_mode() for this
497
     *   argument).  
498
     */
499
    void open_script_file(const char *fname, int quiet, int script_more_mode);
500
501
    /* 
502
     *   Close the script file.  Returns the original MORE mode that was
503
     *   in effect before the script file was opened; this MORE mode
504
     *   should be restored.  
505
     */
506
    int close_script_file();
507
508
    /* 
509
     *   Read a line of text from the keyboard.  Fills in the buffer with
510
     *   a null-terminated UTF-8 string.  Returns zero on success,
511
     *   non-zero on end-of-file reading the console (which usually
512
     *   indicates that the user has closed the application, so we're in
513
     *   the process of terminating; it might also indicate that the
514
     *   user's terminal has been detached, in which case we also probably
515
     *   can't do much except terminate).  
516
     */
517
    int read_line(VMG_ char *buf, size_t buflen);
518
519
    /*
520
     *   Read a line of input with optional timeout.  Fills in the buffer
521
     *   with a null-terminated UTF-8 string.  Returns an OS_EVT_xxx code,
522
     *   according to how the input was terminated:
523
     *   
524
     *   OS_EVT_LINE - the user pressed Return to enter the text
525
     *.  OS_EVT_TIMEOUT - the timeout expired before the user pressed Return
526
     *.  OS_EVT_EOF - an error occurred reading the input
527
     *   
528
     *   This routine is a cover for the low-level os_gets_timeout(), and
529
     *   behaves essentially the same way.  Note in particular that if this
530
     *   routine returns OS_EVT_TIMEOUT, then our read_line_cancel() routine
531
     *   must be called before any output or other display changes can be
532
     *   made, with the exception that another call to read_line_timeout()
533
     *   is always allowed.  
534
     */
535
    int read_line_timeout(VMG_ char *buf, size_t buflen,
536
                          unsigned long timeout, int use_timeout);
537
538
    /*
539
     *   Cancel a line of input in progress, which was interrupted by a
540
     *   timeout in read_line_timeout().  If 'reset' is true, we'll forget
541
     *   any editing state from the prior line.  
542
     */
543
    void read_line_cancel(VMG_ int reset);
544
545
    /*
546
     *   Display a file dialog.  This routine works exactly the same way
547
     *   as os_askfile(), but is implemented here to allow for a formatted
548
     *   text interface on systems where no dialog is available.  
549
     */
550
    int askfile(VMG_ const char *prompt, size_t prompt_len,
551
                char *reply, size_t replen,
552
                int dialog_type, os_filetype_t file_type);
553
554
    /*
555
     *   Display a system dialog.  This routine works exactly the same way
556
     *   as os_input_dialog(), but is implemented here to allow for a
557
     *   formatted text interface on systems where no dialog is available.
558
     */
559
    int input_dialog(VMG_ int icon_id, const char *prompt,
560
                     int standard_button_set,
561
                     const char **buttons, int button_count,
562
                     int default_index, int cancel_index);
563
564
    /* show the MORE prompt and wait for the user to acknowledge it */
565
    virtual void show_more_prompt(VMG0_) = 0;
566
567
    /*
568
     *   Log an event.  This saves the event to the current script log, if
569
     *   there is one, in the proper format for the script.  We return the
570
     *   event code.
571
     *   
572
     *   'evt' is the event type, as an OS_EVT_xxx or VMCON_EVT_xxx code.
573
     *   
574
     *   'param' can be given in the local UI character set or in UTF-8 -
575
     *   specify which it is via 'param_is_utf8'.  We write the file in the
576
     *   local UI character set, so if the parameter is given in UTF-8, we
577
     *   have to translate it.  
578
     */
579
    int log_event(VMG_ int evt,
580
                  const char *param, size_t paramlen, int param_is_utf8);
581
    int log_event(VMG_ int evt)
582
        { return log_event(vmg_ evt, 0, 0, FALSE); }
583
584
    /* read an event from an event script */
585
    int read_event_script(VMG_ int *evt, char *buf, size_t buflen,
586
                          const int *filter, int filter_cnt,
587
                          unsigned long *attrs);
588
589
protected:
590
    /* 
591
     *   Service routine - show MORE prompt on this console.  This can be
592
     *   called from show_more_prompt() when a MORE prompt is desired at all
593
     *   in the subclassed console.  
594
     */
595
    void show_con_more_prompt(VMG0_);
596
    
597
    /* read a line from the script file */
598
    int read_line_from_script(char *buf, size_t buflen, int *evt);
599
600
    /* read the type tag from the next script event */
601
    int read_script_event_type(int *evt, unsigned long *attrs);
602
603
    /* skip to the next line of the script */
604
    void skip_script_line(osfildef *fp);
605
606
    /* 
607
     *   read a script parameter - this reads the rest of the line into the
608
     *   given buffer, and skips to the start of the next line in the script;
609
     *   returns true on success, false if we reach EOF before reading
610
     *   anything 
611
     */
612
    int read_script_param(char *buf, size_t buflen, osfildef *fp);
613
614
    /* internal routine to terminate line reading */
615
    void read_line_done(VMG0_);
616
617
    /* write utf-8 text to a file, mapping to the given file character set */
618
    void write_to_file(osfildef *fp, const char *txt,
619
                       class CCharmapToLocal *map);
620
621
    /* our current display stream */
622
    class CVmFormatter *disp_str_;
623
624
    /* our log stream - this stream is written to the log file, if any */
625
    class CVmFormatterLog *log_str_;
626
    
627
    /* 
628
     *   Flag: the log stream is enabled.  We can temporarily disable the
629
     *   log stream, such as when writing to the statusline stream.  
630
     */
631
    int log_enabled_ : 1;
632
    
633
    /*
634
     *   Flag: display two spaces after a period-like punctuation mark.
635
     *   This should be true if the output should have two spaces after a
636
     *   period, question mark, or exclamation point, false for a single
637
     *   space.  This should generally be true for fixed-width fonts,
638
     *   false for proportional fonts, although some users might prefer to
639
     *   use single-spacing even for fixed-width fonts.  
640
     */
641
    unsigned int doublespace_ : 1;
642
643
    /* 
644
     *   flag: the command log is an event script; if this is set, we log all
645
     *   input events in the tagged <eventscript> file format, otherwise we
646
     *   log just command lines in the old-style ">line" format 
647
     */
648
    unsigned int command_eventscript_ : 1;
649
650
    /*
651
     *   Script-input stack.  Each time we open a script, we create a new
652
     *   stack entry object and link it at the head of the list.  So, the
653
     *   head of the list is the current state, the next element is the
654
     *   enclosing state, and so on.  
655
     */
656
    script_stack_entry *script_sp_;
657
    
658
    /* command log file, if there is one */
659
    osfildef *command_fp_;
660
661
    /* name of the command log file */
662
    char command_fname_[OSFNMAX];
663
};
664
665
/* ------------------------------------------------------------------------ */
666
/*
667
 *   Main system console.  This console is attached to the OS-level primary
668
 *   console.  
669
 */
670
class CVmConsoleMain: public CVmConsole
671
{
672
public:
673
    CVmConsoleMain(VMG0_);
674
    ~CVmConsoleMain();
675
676
    /* get the system banner manager */
677
    class CVmBannerManager *get_banner_manager() const
678
        { return banner_manager_; }
679
680
    /* get the system log console manager */
681
    class CVmLogConsoleManager *get_log_console_manager() const
682
        { return log_console_manager_; }
683
    
684
    /*
685
     *   Switch in or out of statusline mode.  When we're running on the text
686
     *   implementation of the OS layer, we must explicitly switch modes
687
     *   between the main text stream and statusline stream.  'mode' is true
688
     *   to switch to statusline mode, false to switch back to main text
689
     *   mode.  
690
     */
691
    void set_statusline_mode(VMG_ int mode);
692
693
    /* clear the window */
694
    virtual void clear_window(VMG0_);
695
696
    /* set MORE mode */
697
    virtual int set_more_state(int state)
698
    {
699
        int old_state;
700
701
        /* remember the old state */
702
        old_state = G_os_moremode;
703
704
        /* set the new mode */
705
        G_os_moremode = state;
706
707
        /* return the previous state */
708
        return old_state;
709
    }
710
711
    /* get the MORE mode */
712
    virtual int is_more_mode() const { return G_os_moremode; }
713
714
    /* 
715
     *   Flush everything - this flushes not only the main console, but any
716
     *   banner windows we're managing.  This should be called before pausing
717
     *   for input or for a timed delay, to make sure that buffered output in
718
     *   all windows is shown.  
719
     */
720
    void flush_all(VMG_ vm_nl_type nl);
721
722
    /* get the line width of the display device */
723
    virtual int get_line_width() const { return G_os_linewidth; }
724
725
    /* do not allow overrunning the line width on the main console */
726
    virtual int allow_overrun() const { return FALSE; }
727
728
    /* get the page length of the display device */
729
    virtual int get_page_length() const { return G_os_pagelength; }
730
731
    /* show the MORE prompt */
732
    virtual void show_more_prompt(VMG0_) { show_con_more_prompt(vmg0_); }
733
734
protected:
735
    /* main text area display stream */
736
    class CVmFormatterMain *main_disp_str_;
737
738
    /* statusline display stream */
739
    class CVmFormatterStatline *statline_str_;
740
741
    /* 
742
     *   The system banner window manager.  Since the main console is
743
     *   inherently a singleton (as there's only one OS-level primary
744
     *   console), we keep track of the banner manager.  
745
     */
746
    class CVmBannerManager *banner_manager_;
747
748
    /* the system log console manager */
749
    class CVmLogConsoleManager *log_console_manager_;
750
};
751
752
753
/* ------------------------------------------------------------------------ */
754
/*
755
 *   Banner-window console.  
756
 */
757
class CVmConsoleBanner: public CVmConsole
758
{
759
public:
760
    /* create */
761
    CVmConsoleBanner(void *banner_handle, int win_type, unsigned long style);
762
    ~CVmConsoleBanner();
763
764
    /* retrieve our OS-level banner handle */
765
    void *get_os_handle() const { return banner_; }
766
767
    /* get banner information */
768
    int get_banner_info(os_banner_info_t *info);
769
770
    /* clear the window */
771
    virtual void clear_window(VMG0_);
772
773
    /* set MORE mode */
774
    virtual int set_more_state(int state)
775
    {
776
        /* banners never change the global MORE mode state */
777
        return is_more_mode();
778
    }
779
780
    /* get the MORE mode - return the global mode flag */
781
    virtual int is_more_mode() const { return G_os_moremode; }
782
783
    /* show the MORE prompt - does nothing for a banner window */
784
    virtual void show_more_prompt(VMG0_) { show_con_more_prompt(vmg0_); }
785
786
    /* get the line width of the display device */
787
    virtual int get_line_width() const
788
        { return os_banner_get_charwidth(banner_); }
789
790
    /* allow overrunning the line width in a banner */
791
    virtual int allow_overrun() const { return TRUE; }
792
793
    /* get the page length of the display device, for MORE mode */
794
    virtual int get_page_length() const
795
        { return os_banner_get_charheight(banner_); }
796
797
protected:
798
    /* our underlying OS-level banner handle */
799
    void *banner_;
800
801
    /* our window type (an OS_BANNER_TYPE_xxx code) */
802
    int win_type_;
803
};
804
805
/* ------------------------------------------------------------------------ */
806
/*
807
 *   Common base class for log-only consoles 
808
 */
809
class CVmConsoleLogBase: public CVmConsole
810
{
811
public:
812
    /* clear the window - do nothing on a log console */
813
    virtual void clear_window(VMG0_) { }
814
815
    /* set MORE mode - doesn't apply to us */
816
    virtual int set_more_state(int state) { return FALSE; }
817
818
    /* show the MORE prompt - does nothing */
819
    virtual void show_more_prompt(VMG0_) { }
820
821
    /* get the MORE mode */
822
    virtual int is_more_mode() const { return FALSE; }
823
824
    /* get the line width of the display device */
825
    virtual int get_line_width() const { return G_os_linewidth; }
826
827
    /* allow overrunning the line width in a lot file */
828
    virtual int allow_overrun() const { return TRUE; }
829
830
    /* 
831
     *   get the page length of the display device - this is arbitrary, since
832
     *   we don't use MORE mode anyway 
833
     */
834
    virtual int get_page_length() const { return 55; }
835
};
836
837
/*
838
 *   Log console.  This is used to create a console that has no display
839
 *   presence, but simply captures its output directly to a log file.  (This
840
 *   is similar to the log file attached to a regular display console, but
841
 *   this kind of console ONLY has the log file.)  
842
 */
843
class CVmConsoleLog: public CVmConsoleLogBase
844
{
845
public:
846
    /* create */
847
    CVmConsoleLog(const char *fname, osfildef *fp,
848
                  class CCharmapToLocal *cmap, int width);
849
    ~CVmConsoleLog();
850
};
851
852
/*
853
 *   A special console that sends output to the *main* console's log file.
854
 */
855
class CVmConsoleMainLog: public CVmConsoleLogBase
856
{
857
public:
858
    /* create */
859
    CVmConsoleMainLog() { }
860
    ~CVmConsoleMainLog() { }
861
862
    /* we send text to the main console's log */
863
    int format_text(VMG_ const char *p, size_t len)
864
    {
865
        /* send the text to the main console's log file */
866
        return G_console->format_text_to_log(vmg_ p, len);
867
    }
868
};
869
870
871
/* ------------------------------------------------------------------------ */
872
/*
873
 *   constants 
874
 */
875
876
/*
877
 *   HTML lexical analysis modes.  We use these modes for two purposes:
878
 *   
879
 *   First, for tracking our state while doing our own HTML parsing, which we
880
 *   do when the underlying renderer handles only plain text (in which case
881
 *   we must interpret and remove any HTML tags from the stream before
882
 *   sending the stream on to the underlying renderer).
883
 *   
884
 *   Second, for tracking the lexical state in the underlying renderer, when
885
 *   the underlying renderer handles full HTML interpretation.  In this case,
886
 *   we simply pass HTML tags through to the underlying renderer; but even
887
 *   though we don't interpret the tags, we do keep track of the lexical
888
 *   structure of the text, so that we can tell when we're inside a tag and
889
 *   when we're in ordinary text.  Certain operations we apply (such as case
890
 *   conversions with "\^" and "\v" sequences) apply only to ordinary text,
891
 *   so we need to know what's what in the stream text.  
892
 */
893
enum vmconsole_html_state
894
{
895
    VMCON_HPS_NORMAL,                          /* normal text, not in a tag */
896
    VMCON_HPS_TAG,                                  /* parsing inside a tag */
897
    VMCON_HPS_ATTR_NAME,                       /* parsing an attribute name */
898
    VMCON_HPS_ATTR_VAL,                       /* parsing an attribute value */
899
    VMCON_HPS_SQUOTE,                 /* in a single-quoted string in a tag */
900
    VMCON_HPS_DQUOTE,                 /* in a double-quoted string in a tag */
901
    VMCON_HPS_ENTITY_1ST,              /* first character in an entity name */
902
    VMCON_HPS_ENTITY_NUM_1ST,       /* first character of a numeric entitiy */
903
    VMCON_HPS_ENTITY_HEX,                /* in a hexadecimal entitiy number */
904
    VMCON_HPS_ENTITY_DEC,                     /* in a decimal entity number */
905
    VMCON_HPS_ENTITY_NAME,                             /* in a named entity */
906
    VMCON_HPS_MARKUP_END                   /* at last character of a markup */
907
};
908
909
/*
910
 *   HTML parsing mode flag for <BR> tags.  We defer these until we've
911
 *   read the full tag in order to obey an HEIGHT attribute we find.  When
912
 *   we encounter a <BR>, we figure out whether we think we'll need a
913
 *   flush or a blank line; if we find a HEIGHT attribute, we may change
914
 *   this opinion.  
915
 */
916
enum vmconsole_html_br_mode
917
{
918
    HTML_DEFER_BR_NONE,                                  /* no pending <BR> */
919
    HTML_DEFER_BR_FLUSH,                       /* only need to flush output */
920
    HTML_DEFER_BR_BLANK                                /* need a blank line */
921
};
922
923
/*
924
 *   Color/attribute information.  Each character is tagged with its current
925
 *   color and attributes. 
926
 */
927
struct vmcon_color_t
928
{
929
    /* foreground color */
930
    os_color_t fg;
931
932
    /* background color */
933
    os_color_t bg;
934
935
    /* the current OS_ATTR_xxx attributes */
936
    int attr;
937
938
    /* check for equality with another color structure */
939
    int equals(const vmcon_color_t *other) const
940
    {
941
        return (fg == other->fg
942
                && bg == other->bg
943
                && attr == other->attr);
944
    }
945
};
946
947
/* ------------------------------------------------------------------------ */
948
/*
949
 *   Output Buffer Flags.  Each character in the output buffer has an
950
 *   associated flag value associated with it; the flag value is a
951
 *   combination of the bit flags defined here. 
952
 */
953
954
/*
955
 *   Unbreakable character point.  This indicates that a line break is not
956
 *   allowed to follow this character, even if the text at this point would
957
 *   ordinarily allow a soft line break.  Note that this does NOT override a
958
 *   hard line break (i.e., an explicit newline); it merely prevents a soft
959
 *   line break from occurring immediately after this character.
960
 *   
961
 *   We apply this flag to the character immediately preceding an explicit
962
 *   non-breaking space in the text stream.  
963
 */
964
#define VMCON_OBF_NOBREAK    0x01
965
966
/*
967
 *   Breakable character point.  This indicates that a line break is allowed
968
 *   to follow this character, even if the text at this point would not
969
 *   ordinarily allow a soft line break.
970
 *   
971
 *   We apply this flag to the character immediately preceding an explicit
972
 *   zero-width breakable space in the text stream. 
973
 */
974
#define VMCON_OBF_OKBREAK    0x02
975
976
/*
977
 *   Break-anywhere mode.  This indicates that this character is part of a
978
 *   run of break-anywhere text.  In break-anywhere text, we're allowed to
979
 *   break lines between any two characters, except adjacent to explicit
980
 *   non-breaking spaces in the text stream.
981
 *   
982
 *   Note that the NOBREAK flag overrides this flag, because this flag
983
 *   merely indicates the mode, while the NOBREAK flag indicates an explicit
984
 *   non-breaking indicator.  
985
 */
986
#define VMCON_OBF_BREAK_ANY  0x04
987
988
/*
989
 *   Soft hyphenation point.  This flag indicates that the text can be
990
 *   hyphenated immediately following this character, which is to say that
991
 *   we can insert a hyphen following this character and break the line
992
 *   after the hyphen.  If we do not hyphenate here, the soft hyphen has no
993
 *   effect; in particular, no hyphen character appears in the output stream
994
 *   when a soft hyphen is not used as a line break point.  
995
 */
996
#define VMCON_OBF_SHY        0x08
997
998
/*
999
 *   Quoted space.  This indicates that this is a space character that
1000
 *   doesn't collapse in runs of contiguous whitespace. 
1001
 */
1002
#define VMCON_OBF_QSPACE     0x10
1003
1004
1005
/* ------------------------------------------------------------------------ */
1006
/*
1007
 *   Output formatter stream interface.  A formatter stream performs
1008
 *   formatting on a stream of displayed text.
1009
 *   
1010
 *   A given stream of display text might be fed into more than one
1011
 *   formatter stream.  For example, if logging is turned on for a
1012
 *   console, we'll feed the same text to the console's main formatting
1013
 *   stream, which will end up being displayed to the user, and also to
1014
 *   the log file's formatting stream, which will end up written out to
1015
 *   the log file.
1016
 *   
1017
 *   Note that the formatter interface is internal to the console system.
1018
 *   Client code should never need to refer to a formatter object, but
1019
 *   should instead call the console object (CVmConsole).  
1020
 */
1021
1022
/* <TAB> tag alignment types */
1023
enum vmfmt_tab_align_t
1024
{
1025
    VMFMT_TAB_NONE,
1026
    VMFMT_TAB_LEFT,
1027
    VMFMT_TAB_CENTER,
1028
    VMFMT_TAB_RIGHT,
1029
    VMFMT_TAB_DECIMAL
1030
};
1031
1032
/* maximum formatter state stack depth */
1033
const size_t CVMFMT_STACK_MAX = 25;
1034
1035
/* maximum depth of color stack */
1036
const size_t CVFMT_CLRSTK_MAX = 25;
1037
1038
/* maximum length of an attribute name/value */
1039
const size_t CVFMT_MAX_ATTR_NAME = 40;
1040
const size_t CVFMT_MAX_ATTR_VAL = 256;
1041
1042
/* output stream information structure (forward declaration) */
1043
typedef struct out_stream_info out_stream_info;
1044
1045
/* output formatter stream */
1046
class CVmFormatter
1047
{
1048
public:
1049
    CVmFormatter(class CVmConsole *console)
1050
    {
1051
        /* remember our display object */
1052
        console_ = console;
1053
1054
        /* there's no title buffer by default */
1055
        html_title_buf_ = 0;
1056
        html_title_buf_size_ = 0;
1057
1058
        /* we have no horizontal tab table yet (for the HTML mini-parser) */
1059
        tabs_ = 0;
1060
1061
        /* no character mapper yet */
1062
        cmap_ = 0;
1063
    }
1064
1065
    virtual ~CVmFormatter();
1066
    
1067
    /* initialize */
1068
    virtual void init()
1069
    {
1070
        /* start out at the first column */
1071
        linepos_ = 0;
1072
        linecol_ = 0;
1073
        linebuf_[0] = '\0';
1074
        is_continuation_ = FALSE;
1075
1076
        /* start out in normal text color */
1077
        cur_color_.fg = OS_COLOR_P_TEXT;
1078
        cur_color_.bg = OS_COLOR_P_TRANSPARENT;
1079
        cur_color_.attr = 0;
1080
        os_color_ = cur_color_;
1081
1082
        /* no pending tab yet */
1083
        pending_tab_align_ = VMFMT_TAB_NONE;
1084
1085
        /* start out at the first line */
1086
        linecnt_ = 0;
1087
1088
        /* we're not in either "caps", "nocaps", or "allcaps" mode yet */
1089
        capsflag_ = nocapsflag_ = allcapsflag_ = FALSE;
1090
1091
        /* 
1092
         *   set the initial buffer flags: start out in word-break (not
1093
         *   break-anywhere) more 
1094
         */
1095
        cur_flags_ = 0;
1096
1097
        /* 
1098
         *   start in normal spacing mode - treat runs of whitespace as
1099
         *   equivalent to a single space 
1100
         */
1101
        obey_whitespace_ = FALSE;
1102
1103
        /* start out in HTML interpretation mode */
1104
        literal_mode_ = FALSE;
1105
1106
        /* presume we won't have OS-level line wrapping */
1107
        os_line_wrap_ = FALSE;
1108
1109
        /* we haven't flushed a new line yet */
1110
        just_did_nl_ = FALSE;
1111
1112
        /* assume that the underlying system is not HTML-enabled */
1113
        html_target_ = FALSE;
1114
1115
        /* presume this target accepts OS highlighting sequences */
1116
        plain_text_target_ = FALSE;
1117
1118
        /* 
1119
         *   start out in "normal" lexical state in our parser and in the
1120
         *   underlying output stream 
1121
         */
1122
        html_parse_state_ = VMCON_HPS_NORMAL;
1123
        html_passthru_state_ = VMCON_HPS_NORMAL;
1124
1125
        /* not in an ignored tag yet (-> nesting depth is zero) */
1126
        html_in_ignore_ = 0;
1127
1128
        /* not in title mode yet (-> nesting depth is zero) */
1129
        html_in_title_ = 0;
1130
1131
        /* not yet deferring line breaks */
1132
        html_defer_br_ = HTML_DEFER_BR_NONE;
1133
1134
        /* not yet in quotes (-> nesting depth is zero) */
1135
        html_quote_level_ = 0;
1136
1137
        /* we're not parsing inside any tags yet */
1138
        html_allow_alt_ = FALSE;
1139
        html_allow_color_ = FALSE;
1140
        html_in_body_ = FALSE;
1141
        html_in_tab_ = FALSE;
1142
        html_in_wrap_ = FALSE;
1143
1144
        /* assume we're a normal display stream */
1145
        is_disp_stream_ = TRUE;
1146
1147
        /* no stacked colors yet */
1148
        color_sp_ = 0;
1149
    }
1150
1151
    /* get/set obey-whitespace mode */
1152
    int get_obey_whitespace() const { return obey_whitespace_ != 0; }
1153
    void set_obey_whitespace(int f) { obey_whitespace_ = (f != 0); }
1154
1155
    /* turn on CAPS mode */
1156
    void caps()
1157
    {
1158
        /* turn on CAPS mode */
1159
        capsflag_ = TRUE;
1160
1161
        /* turn off the NOCAPS and ALLCAPS modes */
1162
        nocapsflag_ = FALSE;
1163
        allcapsflag_ = FALSE;
1164
    }
1165
1166
    /* turn on NOCAPS mode */
1167
    void nocaps()
1168
    {
1169
        /* turn on NOCAPS mode */
1170
        nocapsflag_ = TRUE;
1171
        
1172
        /* turn off CAPS and ALLCAPS mode */
1173
        capsflag_ = FALSE;
1174
        allcapsflag_ = FALSE;
1175
    }
1176
    
1177
    /* turn on or off ALLCAPS mode */
1178
    void allcaps(int all_caps)
1179
    {
1180
        /* set the ALLCAPS flag */
1181
        allcapsflag_ = all_caps;
1182
        
1183
        /* clear the CAPS and NOCAPS flags */
1184
        capsflag_ = FALSE;
1185
        nocapsflag_ = FALSE;
1186
    }
1187
1188
    /* 
1189
     *   Display a string of a given byte-length.  The text is given in
1190
     *   UTF-8 format.  We'll interpret embedded control codes (newlines,
1191
     *   blank lines, caps flags, etc); depending on the display mode, we
1192
     *   might also interpret HTML markup sequences.  
1193
     */
1194
    int format_text(VMG_ const char *s, size_t len);
1195
1196
    /* set the text attributes for subsequent format_text() displays */
1197
    void set_text_attr(VMG_ int attr)
1198
    {
1199
        /* remember the new text attributes */
1200
        cur_color_.attr = attr;
1201
    }
1202
1203
    /* set the text color for subsequent format_text() displays */
1204
    void set_text_color(VMG_ os_color_t fg, os_color_t bg)
1205
    {
1206
        /* remember the new text colors */
1207
        cur_color_.fg = fg;
1208
        cur_color_.bg = bg;
1209
    }
1210
1211
    /* write a blank line to the stream */
1212
    void write_blank_line(VMG0_);
1213
1214
    /*
1215
     *   Flush the current line to the display, using the given type of
1216
     *   line termination. 
1217
     */
1218
    void flush(VMG_ vm_nl_type nl);
1219
1220
    /* clear our buffers */
1221
    void empty_buffers(VMG0_);
1222
1223
    /* immediately update the display window */
1224
    void update_display(VMG0_);
1225
1226
    /* 
1227
     *   determine if I'm an HTML target - this returns true if the
1228
     *   underyling renderer accepts HTML, in which case we write HTML
1229
     *   markups directly to the os_xxx routines (os_printz, for example).
1230
     */
1231
    int is_html_target() const { return html_target_; }
1232
1233
    /* reset the MORE prompt line counter */
1234
    virtual void reset_line_count(int clearing) { linecnt_ = 0; }
1235
1236
    /*
1237
     *   Note that we read a line from the keyboard with echo.  Reading a
1238
     *   line displays a carriage return without our help, taking us back to
1239
     *   the first column - we need to know this so that we can deal
1240
     *   properly with output on the following line.  
1241
     */
1242
    void note_input_line()
1243
    {
1244
        /* 
1245
         *   a CR/LF will have been echoed automatically by the input
1246
         *   reader, which takes us back to the first column 
1247
         */
1248
        linecol_ = 0;
1249
1250
        /* note that we effectively just wrote a newline */
1251
        just_did_nl_ = TRUE;
1252
    }
1253
1254
    /* -------------------------------------------------------------------- */
1255
    /*
1256
     *   Virtual interface to underlying OS renderer 
1257
     */
1258
1259
    /* turn HTML mode on/off in the underlying OS-level renderer */
1260
    virtual void start_html_in_os() = 0;
1261
    virtual void end_html_in_os() = 0;
1262
1263
    /* set the text color */
1264
    virtual void set_os_text_color(os_color_t fg, os_color_t bg) = 0;
1265
1266
    /* set the body color */
1267
    virtual void set_os_body_color(os_color_t) = 0;
1268
1269
    /* set text attributes */
1270
    virtual void set_os_text_attr(int attr) = 0;
1271
1272
    /* 
1273
     *   display text to the OS window; the text is given in the local
1274
     *   character set 
1275
     */
1276
    virtual void print_to_os(const char *txt) = 0;
1277
1278
    /* flush the underlying OS-level rendere */
1279
    virtual void flush_to_os() = 0;
1280
1281
    /* set the window title in the OS layer */
1282
    virtual void set_title_in_os(const char *txt) = 0;
1283
1284
    /* set a new character mapper */
1285
    void set_charmap(class CCharmapToLocal *cmap);
1286
1287
protected:
1288
    /*
1289
     *   Write a line (or a partial line) of text to the stream, using the
1290
     *   indicated line breaking.  The text is given as wide Unicode
1291
     *   characters.  
1292
     */
1293
    void write_text(VMG_ const wchar_t *txt, size_t cnt,
1294
                    const vmcon_color_t *colors, vm_nl_type nl);
1295
1296
    /* write a tab to the stream */
1297
    void write_tab(VMG_ int indent, int multiple);
1298
1299
    /* flush the current line, with or without padding */
1300
    void flush_line(VMG_ int padding);
1301
1302
    /* 
1303
     *   Buffer a character of output.  The character is presented to us as a
1304
     *   wide Unicode character.  We'll expand this character with the local
1305
     *   character mapping's expansion rules, then add the expansion to our
1306
     *   output buffer, performing word-wrapping as needed.  
1307
     */
1308
    void buffer_char(VMG_ wchar_t c);
1309
1310
    /* 
1311
     *   buffer an expanded character - we'll buffer the given unicode
1312
     *   character, with no further expansion 
1313
     */
1314
    void buffer_expchar(VMG_ wchar_t c);
1315
1316
    /* buffer a rendered expanded character */
1317
    void buffer_rendered(wchar_t c, unsigned char flags, int wid);
1318
1319
    /* 
1320
     *   Buffer a string of output to the stream.  The string is in UTF-8
1321
     *   format. 
1322
     */
1323
    void buffer_string(VMG_ const char *txt);
1324
1325
    /* buffer a wide unicode character string */
1326
    void buffer_wstring(VMG_ const wchar_t *txt);
1327
1328
    /* get the next wide unicode character in a UTF8-encoded string */
1329
    static wchar_t next_wchar(const char **s, size_t *len);
1330
1331
    /* 
1332
     *   Determine if we should use MORE mode in the formatter layer; if
1333
     *   this returns true, we handle MORE prompting ourselves, otherwise
1334
     *   we handle it through the OS layer.
1335
     *   
1336
     *   The default version of this function is implemented in the output
1337
     *   formatter configuration module.  This function can also be
1338
     *   overridden in a subclass (for example, a log stream never uses
1339
     *   MORE mode, no matter what configuration we're using).  
1340
     */
1341
    virtual int formatter_more_mode() const;
1342
1343
    /*
1344
     *   Get the maximum column to store in our internal buffer.  If we're
1345
     *   doing our own line breaking, we'll break off the buffer at the
1346
     *   actual line width of the underlying console.  If we're doing OS
1347
     *   line wrapping, we'll simply fill up our internal buffer to its
1348
     *   maximum size, since the flushing points are irrelevant to the line
1349
     *   wrapping the underlying OS console will be doing and hence we might
1350
     *   as well buffer as much as we can for efficiency.  
1351
     */
1352
    virtual int get_buffer_maxcol() const
1353
    {
1354
        /* 
1355
         *   if the OS is doing the line wrapping, use the full buffer;
1356
         *   otherwise, use the actual line width from the underyling
1357
         *   console 
1358
         */
1359
        if (os_line_wrap_)
1360
            return OS_MAXWIDTH;
1361
        else
1362
            return console_->get_line_width();
1363
    }
1364
1365
    /*
1366
     *   Determine if the underlying stream interprets HTML markups.  We
1367
     *   call this during initialization to set our html_target_ member
1368
     *   (we cache the result since we check it frequently).
1369
     *   
1370
     *   This is implemented in the formatter configuration module.  
1371
     */
1372
    virtual int get_init_html_target() const;
1373
1374
    /* -------------------------------------------------------------------- */
1375
    /*
1376
     *   HTML mini-parser.  This only needs to be implemented when linking
1377
     *   with non-HTML osifc implementations; when osifc provides HTML
1378
     *   parsing, we don't need to do any HTML parsing here, so these can use
1379
     *   dummy implementations. 
1380
     */
1381
1382
    /*
1383
     *   Resume text-only HTML mini-parser.  This is called when we start
1384
     *   writing a new string and discover that we're parsing inside an HTML
1385
     *   tag in our mini-parser.
1386
     *   
1387
     *   Returns the next character after the run of text we parse.  
1388
     */
1389
    wchar_t resume_html_parsing(VMG_ wchar_t c,
1390
                                const char **sp, size_t *slenp);
1391
    
1392
    /*
1393
     *   Parse the beginning HTML markup.  This is called when we are
1394
     *   scanning a '<' or '&' character in output text, and we're in HTML
1395
     *   mode, and the underlying target doesn't support HTML parsing.
1396
     *   Returns the next character to process after we finish our initial
1397
     *   parsing.  
1398
     */
1399
    wchar_t parse_html_markup(VMG_ wchar_t c,
1400
                              const char **sp, size_t *slenp);
1401
1402
    /* 
1403
     *   Expand any pending tab.  This should be called when we're doing
1404
     *   HTML mini-parsing, and we see a <TAB> tag or we reach the end of an
1405
     *   output line.  We'll expand any pending RIGHT/CENTER tab by going
1406
     *   back to the tab's location and inserting the necessary number of
1407
     *   spaces now that we know the extent of the text affected.
1408
     *   
1409
     *   If 'allow_anon' is true, we will allow "anonymous" tabs, which is
1410
     *   to say tabs with no ID attribute.  Anonymous tabs allow text to be
1411
     *   tabbed relative to the full width of the line, but these are
1412
     *   meaningful only with normal line endings; when a line is flushed
1413
     *   before the end of the line is reached (because the line will be
1414
     *   used for a prompt, for example), anonymous tabs should not be
1415
     *   expanded with the line.  
1416
     */
1417
    void expand_pending_tab(VMG_ int allow_anon);
1418
1419
    /* 
1420
     *   find a tab definition object; if 'create' it true and the specified
1421
     *   tab doesn't exist, we'll create a new tab object
1422
     */
1423
    class CVmFmtTabStop *find_tab(wchar_t *id, int create);
1424
1425
    /* service routine - translate a wide character string to an integer */
1426
    static int wtoi(const wchar_t *p);
1427
1428
    /* parse a COLOR attribute of a FONT tag */
1429
    int parse_color_attr(VMG_ const wchar_t *val, os_color_t *result);
1430
1431
    /* push the current color for later restoration */
1432
    void push_color()
1433
    {
1434
        /* 
1435
         *   add a level to the color stack, if possible; if it's not
1436
         *   possible, assume we've lost an end tag somewhere, so rotate the
1437
         *   entire stack down a level 
1438
         */
1439
        if (color_sp_ == CVFMT_CLRSTK_MAX)
1440
        {
1441
            /* take everything down a level */
1442
            memmove(color_stack_, color_stack_ + 1,
1443
                    (CVFMT_CLRSTK_MAX - 1)*sizeof(color_stack_[0]));
1444
            --color_sp_;
1445
        }
1446
1447
        /* save the current color in the stack */
1448
        color_stack_[color_sp_++] = cur_color_;
1449
    }
1450
1451
    /* pop the current color */
1452
    void pop_color()
1453
    {
1454
        /* 
1455
         *   if we're at the bottom of the stack, we must have had too many
1456
         *   close tags; ignore the extra operation 
1457
         */
1458
        if (color_sp_ != 0)
1459
        {
1460
            /* restore the next color down */
1461
            cur_color_ = color_stack_[--color_sp_];
1462
        }
1463
    }
1464
1465
    /* 
1466
     *   Process a <nolog> tag.  By default, we ignore this tag entirely.
1467
     *   Log streams should hide the contents of this tag.  
1468
     */
1469
    virtual void process_nolog_tag(int /*is_end_tag*/)
1470
    {
1471
        /* by default, ignore this tag */
1472
    }
1473
1474
    /*
1475
     *   Process a <log> tag.  By default, we hide the contents of this tag;
1476
     *   this tag marks text that is to be shown only in a log stream. 
1477
     */
1478
    virtual void process_log_tag(int is_end_tag)
1479
    {
1480
        /* turn hiding on or off as appropriate */
1481
        if (is_end_tag)
1482
            --html_in_ignore_;
1483
        else
1484
            ++html_in_ignore_;
1485
    }
1486
1487
    /* -------------------------------------------------------------------- */
1488
    /*
1489
     *   Member variables 
1490
     */
1491
        
1492
    /* the console that owns this formatter stream */
1493
    class CVmConsole *console_;
1494
1495
    /* our character mapper */
1496
    class CCharmapToLocal *cmap_;
1497
    
1498
    /* current line position and output column */
1499
    int linepos_;
1500
    int linecol_;
1501
1502
    /* 
1503
     *   flag: the current buffer is a "continuation" line; that is, we've
1504
     *   already flushed a partial line to the display without moving to a
1505
     *   new line vertically, and the current buffer will be displayed on the
1506
     *   same line on the terminal, to the right of the previously-displayed
1507
     *   material 
1508
     */
1509
    int is_continuation_;
1510
1511
    /* number of lines on the screen (since last MORE prompt) */
1512
    int linecnt_;
1513
1514
    /* 
1515
     *   Output buffer.  We keep the output buffer as wide Unicode
1516
     *   characters, and translate to the local character set when we
1517
     *   flush the buffer and send the text to the os_xxx routines for
1518
     *   display. 
1519
     */
1520
    wchar_t linebuf_[OS_MAXWIDTH + 1];
1521
1522
    /* 
1523
     *   Output buffer character colors.  We keep track of the display color
1524
     *   of each character in the buffer.  
1525
     */
1526
    vmcon_color_t colorbuf_[OS_MAXWIDTH + 1];
1527
1528
    /* 
1529
     *   output buffer flags - we keep a flag value for each character in
1530
     *   the output buffer 
1531
     */
1532
    unsigned char flagbuf_[OS_MAXWIDTH + 1];
1533
1534
    /* current attribute name/value buffers */
1535
    wchar_t attrname_[CVFMT_MAX_ATTR_NAME];
1536
    wchar_t attrval_[CVFMT_MAX_ATTR_VAL];
1537
    wchar_t attr_qu_;
1538
1539
    /* current color of characters being added to our buffer */
1540
    vmcon_color_t cur_color_;
1541
1542
    /* color of last OS output (via write_text) */
1543
    vmcon_color_t os_color_;
1544
1545
    /* stack of color attributes, saved for nested tags */
1546
    vmcon_color_t color_stack_[CVFMT_CLRSTK_MAX];
1547
    size_t color_sp_;
1548
1549
    /* 
1550
     *   Current output character flags.  This is the base value to write to
1551
     *   flagbuf_ for each character we output, given the current mode
1552
     *   settings.  
1553
     */
1554
    unsigned char cur_flags_;
1555
1556
    /* obey-whitespace mode - treat whitespace as literal */
1557
    unsigned int obey_whitespace_ : 1;
1558
1559
    /* literal mode - ignore HTML markups, pass everything literally */
1560
    unsigned int literal_mode_ : 1;
1561
1562
    /* CAPS mode - next character output is converted to upper-case */
1563
    unsigned int capsflag_ : 1;
1564
1565
    /* NOCAPS mode - next character output is converted to lower-case */
1566
    unsigned int nocapsflag_ : 1;
1567
1568
    /* ALLCAPS mode - all characters output are converted to upper-case */
1569
    unsigned int allcapsflag_ : 1;
1570
1571
    /* flag indicating that we just flushed a new line */
1572
    unsigned int just_did_nl_ : 1;
1573
1574
    /*
1575
     *   Flag indicating that the underlying output system wants to
1576
     *   receive its output as HTML.
1577
     *   
1578
     *   If this is true, we'll pass through HTML to the underlying output
1579
     *   system, and in addition generate HTML sequences for certain
1580
     *   TADS-native escapes (for example, we'll convert the "\n" sequence
1581
     *   to a <BR> sequence).
1582
     *   
1583
     *   If this is false, we'll do just the opposite: we'll remove HTML
1584
     *   from the output stream and convert it into normal text sequences. 
1585
     */
1586
    unsigned int html_target_ : 1;
1587
1588
    /*
1589
     *   Flag indicating that the target uses plain text.  If this flag is
1590
     *   set, we won't add the OS escape codes for highlighted characters. 
1591
     */
1592
    unsigned int plain_text_target_ : 1;
1593
1594
    /* 
1595
     *   flag: the underlying OS layer handles line wrapping, so we never
1596
     *   need to write newlines when flushing our line buffer except when
1597
     *   we want to indicate a hard line break 
1598
     */
1599
    unsigned int os_line_wrap_ : 1;
1600
1601
    /* 
1602
     *   Current lexical analysis state for our own HTML parsing.  This is
1603
     *   used to track our HTML state when we have an underlying plain text
1604
     *   renderer.  
1605
     */
1606
    vmconsole_html_state html_parse_state_;
1607
1608
    /*
1609
     *   Current lexical analysis mode for the text stream going to the
1610
     *   underlying renderer.  This is used to track the lexical structure of
1611
     *   the stream when we're passing HTML tags through to the underlying
1612
     *   renderer.  
1613
     */
1614
    vmconsole_html_state html_passthru_state_;
1615
1616
    /* <BR> defer mode */
1617
    vmconsole_html_br_mode html_defer_br_;
1618
1619
    /* 
1620
     *   HTML "ignore" mode - we suppress all output when parsing the
1621
     *   contents of a <TITLE> or <ABOUTBOX> tag.  This is a counter that
1622
     *   keeps track of the nesting depth for ignored tags.  
1623
     */
1624
    int html_in_ignore_;
1625
1626
    /*
1627
     *   HTML <TITLE> mode - when we're in this mode, we're gathering the
1628
     *   title (i.e., we're inside a <TITLE> tag's contents).  We'll copy
1629
     *   characters to the title buffer rather than the normal output
1630
     *   buffer, and then call os_set_title() when we reach the </TITLE>
1631
     *   tag.  This is a counter that keeps track of the nesting depth of
1632
     *   <TITLE> tags.  
1633
     */
1634
    int html_in_title_;
1635
1636
    /* buffer for the title */
1637
    char *html_title_buf_;
1638
    size_t html_title_buf_size_;
1639
1640
    /* pointer to next available character in title buffer */
1641
    char *html_title_ptr_;
1642
1643
    /* 
1644
     *   quoting level - this is a counter that keeps track of the nesting
1645
     *   depth of <Q> tags 
1646
     */
1647
    int html_quote_level_;
1648
1649
    /*
1650
     *   Parsing mode flag for ALT attributes.  If we're parsing a tag
1651
     *   that allows ALT, such as IMG or SOUND, we'll set this flag, then
1652
     *   insert the ALT text if we encounter it during parsing.  
1653
     */
1654
    unsigned int html_allow_alt_ : 1;
1655
1656
    /* parsing mode flag for COLOR attributes */
1657
    unsigned int html_allow_color_ : 1;
1658
1659
    /* parsing a BODY tag's attributes */
1660
    unsigned int html_in_body_ : 1;
1661
1662
    /* parsing a TAB tag's attributes */
1663
    unsigned int html_in_tab_ : 1;
1664
1665
    /* parsing a WRAP tag's attributes */
1666
    unsigned int html_in_wrap_ : 1;
1667
1668
    /* hash table of <TAB> objects */
1669
    class CVmHashTable *tabs_;
1670
1671
    /* characteristics of TAB tag we're defining */
1672
    class CVmFmtTabStop *new_tab_entry_;
1673
    vmfmt_tab_align_t new_tab_align_;
1674
    wchar_t new_tab_dp_;
1675
1676
    /* 
1677
     *   Characteristics of pending tab.  We must handle this tab when we
1678
     *   reach the next <TAB> or the end of the current line.  If
1679
     *   pending_tab_align_ is VMFMT_TAB_NONE, it indicates that there is no
1680
     *   pending tab (since a pending tab always requires alignment).  
1681
     */
1682
    vmfmt_tab_align_t pending_tab_align_;
1683
    class CVmFmtTabStop *pending_tab_entry_;
1684
    wchar_t pending_tab_dp_;
1685
1686
    /* 
1687
     *   starting column of pending tab - this is the output column where we
1688
     *   were writing when the pending tab was encountered, so this is the
1689
     *   column where we insert spaces for the tab 
1690
     */
1691
    int pending_tab_start_;
1692
1693
    /* 
1694
     *   color/attributes active at start of pending tab - this is the color
1695
     *   we'll use for the spaces we insert when we insert the tab 
1696
     */
1697
    vmcon_color_t pending_tab_color_;
1698
1699
    /* 
1700
     *   Flag: this is a display stream.  Other types of streams (such as
1701
     *   log file streams) should set this to false. 
1702
     */
1703
    unsigned int is_disp_stream_ : 1;
1704
};
1705
1706
/* ------------------------------------------------------------------------ */
1707
/*
1708
 *   Formatter for display windows 
1709
 */
1710
class CVmFormatterDisp: public CVmFormatter
1711
{
1712
public:
1713
    CVmFormatterDisp(class CVmConsole *console)
1714
        : CVmFormatter(console)
1715
    {
1716
    }
1717
1718
    /* initialize */
1719
    void init()
1720
    {
1721
        /* inherit base class initialization */
1722
        CVmFormatter::init();
1723
1724
        /* 
1725
         *   if we're compiled for HTML mode, set the standard output
1726
         *   stream so that it knows it has an HTML target - this will
1727
         *   ensure that HTML tags are passed through to the underlying
1728
         *   stream, and that we generate HTML equivalents for our own
1729
         *   control sequences 
1730
         */ 
1731
        html_target_ = get_init_html_target();
1732
1733
        /* 
1734
         *   since we always use HTML mode, turn on HTML mode in the
1735
         *   underlying OS window if our underlying OS renderer is HTML-aware
1736
         */
1737
        if (html_target_)
1738
            start_html_in_os();
1739
    }
1740
};
1741
1742
/* ------------------------------------------------------------------------ */
1743
/*
1744
 *   Formatter subclass for the main display 
1745
 */
1746
class CVmFormatterMain: public CVmFormatterDisp
1747
{
1748
public:
1749
    CVmFormatterMain(class CVmConsole *console, size_t html_title_buf_size)
1750
        : CVmFormatterDisp(console)
1751
    {
1752
        /* allocate a title buffer */
1753
        html_title_buf_size_ = html_title_buf_size;
1754
        if (html_title_buf_size_ != 0)
1755
            html_title_buf_ = (char *)t3malloc(html_title_buf_size_);
1756
    }
1757
    
1758
    ~CVmFormatterMain()
1759
    {
1760
        /* delete our title buffer */
1761
        if (html_title_buf_ != 0)
1762
            t3free(html_title_buf_);
1763
    }
1764
1765
    /* initialize */
1766
    void init()
1767
    {
1768
        /* inherit base class initialization */
1769
        CVmFormatterDisp::init();
1770
1771
        /* remember the OS line wrap setting from the console */
1772
        os_line_wrap_ = get_os_line_wrap();
1773
    }
1774
1775
    /* set the window title in the OS layer */
1776
    virtual void set_title_in_os(const char *txt)
1777
    {
1778
        /* set the window title */
1779
        os_set_title(txt);
1780
    }
1781
1782
protected:
1783
    /* 
1784
     *   Determine if the main console uses OS-level line wrapping - if this
1785
     *   is returns true, then an output formatter on this console will not
1786
     *   insert a newline at the end of a line that it's flushing for word
1787
     *   wrapping, but will instead let the underlying OS display layer
1788
     *   handle the wrapping.
1789
     *   
1790
     *   The OS line wrapping status is a PERMANENT feature of the console,
1791
     *   so it is safe for the formatter to query this during initialization
1792
     *   and cache the value.  
1793
     */
1794
    static int get_os_line_wrap();
1795
1796
    /* turn HTML mode on/off in the underlying OS-level renderer */
1797
    virtual void start_html_in_os();
1798
    virtual void end_html_in_os();
1799
1800
    /* display text directly to the OS renderer */
1801
    virtual void print_to_os(const char *txt)
1802
    {
1803
        /* display the text on the primary OS console */
1804
        os_printz(txt);
1805
    }
1806
1807
    /* flush the underlying OS-level renderer */
1808
    virtual void flush_to_os() { os_flush(); }
1809
1810
    /* 
1811
     *   set text attributes for subsequent text directly in the underlying
1812
     *   OS window 
1813
     */
1814
    virtual void set_os_text_attr(int attr)
1815
    {
1816
        /* if the target isn't in 'plain' mode, set the attributes */
1817
        if (!plain_text_target_)
1818
            os_set_text_attr(attr);
1819
    }
1820
1821
    /* set the text color in the underlying OS window */
1822
    virtual void set_os_text_color(os_color_t fg, os_color_t bg)
1823
    {
1824
        /* 
1825
         *   if the target is in 'plain' mode, don't use colors; otherwise,
1826
         *   ask the console to do the work 
1827
         */
1828
        if (!plain_text_target_)
1829
            os_set_text_color(fg, bg);
1830
    }
1831
1832
    /* set the "body" color in the underlying OS window */
1833
    virtual void set_os_body_color(os_color_t color)
1834
    {
1835
        /* if not in "plain" mode, set the color */
1836
        if (!plain_text_target_)
1837
            os_set_screen_color(color);
1838
    }
1839
1840
};
1841
1842
/* ------------------------------------------------------------------------ */
1843
/*
1844
 *   Formatter subclass for the status line 
1845
 */
1846
class CVmFormatterStatline: public CVmFormatterDisp
1847
{
1848
public:
1849
    CVmFormatterStatline(class CVmConsole *console)
1850
        : CVmFormatterDisp(console)
1851
    {
1852
    }
1853
1854
    /* we never use 'more' mode in the status line */
1855
    virtual int formatter_more_mode() const { return FALSE; }
1856
1857
    /* HTML mode has no effect on the status line */
1858
    virtual void start_html_in_os() { }
1859
    virtual void end_html_in_os() { }
1860
1861
    /* text colors and attributes are not used in the status line */
1862
    virtual void set_os_text_color(os_color_t, os_color_t) { }
1863
    virtual void set_os_body_color(os_color_t) { }
1864
    virtual void set_os_text_attr(int) { }
1865
1866
    /* text displayed in the status line goes directly to the main console */
1867
    virtual void print_to_os(const char *txt) { os_printz(txt); }
1868
1869
    /* flushing the status line simply flushes the main text stream */
1870
    virtual void flush_to_os() { os_flush(); }
1871
1872
    /* titles have no effect in the status line */
1873
    virtual void set_title_in_os(const char *) { }
1874
};
1875
1876
/* ------------------------------------------------------------------------ */
1877
/*
1878
 *   Formatter subclass for banner windows
1879
 */
1880
class CVmFormatterBanner: public CVmFormatterDisp
1881
{
1882
public:
1883
    CVmFormatterBanner(void *banner, class CVmConsole *console,
1884
                       int win_type, unsigned long style)
1885
        : CVmFormatterDisp(console)
1886
    {
1887
        /* remember my OS banner handle */
1888
        banner_ = banner;
1889
1890
        /* remember my window type */
1891
        win_type_ = win_type;
1892
1893
        /* remember the banner style */
1894
        style_ = style;
1895
    }
1896
1897
    /* initialize */
1898
    void init_banner(int os_line_wrap, int obey_whitespace, int literal_mode)
1899
    {
1900
        /* inherit base class initialization */
1901
        CVmFormatterDisp::init();
1902
1903
        /* remember the OS line wrapping mode in our underlying OS window */
1904
        os_line_wrap_ = os_line_wrap;
1905
1906
        /* remember the whitespace setting */
1907
        obey_whitespace_ = obey_whitespace;
1908
1909
        /* remember the literal-mode setting */
1910
        literal_mode_ = literal_mode;
1911
    }
1912
1913
    /* set the window title in the OS layer (does nothing for a banner) */
1914
    virtual void set_title_in_os(const char *) { }
1915
1916
    /* reset the MORE prompt line counter */
1917
    virtual void reset_line_count(int clearing)
1918
    {
1919
        /* 
1920
         *   To ensure we always keep a line of context when we page-forward
1921
         *   from a MORE prompt, start the line counter at 2.  Note that we
1922
         *   do this in banner windows, but not in the main window, because -
1923
         *   for historical reasons - the OS layer tells us the *paging* size
1924
         *   for the main window, but the *actual* height for banner window.
1925
         *   Note that we don't want to handle this adjustment in our own
1926
         *   page length calculation, because doing so would cause the
1927
         *   *first* MORE prompt (from a cleared window) to show up too
1928
         *   early.
1929
         *   
1930
         *   When we're clearing the screen, reset to zero, since we have no
1931
         *   context to retain.  
1932
         */
1933
        linecnt_ = (clearing ? 0 : 1);
1934
    }
1935
1936
protected:
1937
    /* 
1938
     *   Use MORE mode in a banner window if we have the MORE-mode banner
1939
     *   window style, AND the base display banner would use MORE mode.  The
1940
     *   latter check tests to see if we handle MORE mode at the OS level or
1941
     *   in the formatter.  
1942
     */
1943
    virtual int formatter_more_mode() const
1944
    {
1945
        return ((style_ & OS_BANNER_STYLE_MOREMODE) != 0
1946
                && CVmFormatterDisp::formatter_more_mode());
1947
    }
1948
1949
    /* display text directly to the OS renderer */
1950
    virtual void print_to_os(const char *txt)
1951
    {
1952
        /* display the text on our OS banner window */
1953
        os_banner_disp(banner_, txt, strlen(txt));
1954
    }
1955
1956
    /* turn HTML mode on/off in the underlying OS-level renderer */
1957
    virtual void start_html_in_os() { os_banner_start_html(banner_); }
1958
    virtual void end_html_in_os() { os_banner_end_html(banner_); }
1959
1960
    /* set the text color */
1961
    virtual void set_os_text_color(os_color_t fg, os_color_t bg)
1962
    {
1963
        /* set the color in the banner */
1964
        os_banner_set_color(banner_, fg, bg);
1965
    }
1966
1967
    /* set the body color */
1968
    virtual void set_os_body_color(os_color_t color)
1969
    {
1970
        /* set the color in the banner */
1971
        os_banner_set_screen_color(banner_, color);
1972
    }
1973
1974
    /* set text attributes */
1975
    virtual void set_os_text_attr(int attr)
1976
    {
1977
        /* set the attributes on the underlying OS-level banner */
1978
        os_banner_set_attr(banner_, attr);
1979
    }
1980
1981
    /* flush the underlying OS-level renderer */
1982
    virtual void flush_to_os()
1983
    {
1984
        /* flush the OS banner window */
1985
        os_banner_flush(banner_);
1986
    }
1987
1988
    /* determine if the target supports HTML */
1989
    virtual int get_init_html_target() const
1990
    {
1991
        /* if we're a text grid, the underlying window does not use HTML */
1992
        if (win_type_ == OS_BANNER_TYPE_TEXTGRID)
1993
            return FALSE;
1994
1995
        /* defer to the inherited determination in other cases */
1996
        return CVmFormatterDisp::get_init_html_target();
1997
    }
1998
1999
    /* my OS banner handle */
2000
    void *banner_;
2001
2002
    /* my OS window type (OS_BANNER_TYPE_xxx) */
2003
    int win_type_;
2004
2005
    /* banner style (a combination of OS_BANNER_STLE_xxx bit flags) */
2006
    unsigned long style_;
2007
};
2008
2009
/* ------------------------------------------------------------------------ */
2010
/*
2011
 *   Formatter subclass for the log file 
2012
 */
2013
class CVmFormatterLog: public CVmFormatter
2014
{
2015
    friend class CVmConsole;
2016
    friend class CVmConsoleLog;
2017
    
2018
public:
2019
    CVmFormatterLog(class CVmConsole *console, int width)
2020
        : CVmFormatter(console)
2021
    {
2022
        /* we have no log file yet */
2023
        logfname_ = 0;
2024
        logfp_ = 0;
2025
2026
        /* remember our width */
2027
        width_ = width;
2028
    }
2029
2030
    ~CVmFormatterLog();
2031
    
2032
    /* initialize */
2033
    void init()
2034
    {
2035
        /* inherit base class initialization */
2036
        CVmFormatter::init();
2037
        
2038
        /* use plain text in the log file stream */
2039
        plain_text_target_ = TRUE;
2040
        
2041
        /* we're not a display stream */
2042
        is_disp_stream_ = FALSE;
2043
        
2044
        /* we're not an HTML formatter */
2045
        html_target_ = FALSE;
2046
2047
        /* 
2048
         *   we use our own internal line wrapping, since our underlying
2049
         *   display layer is simply dumping to a text file 
2050
         */
2051
        os_line_wrap_ = FALSE;
2052
        
2053
        /* no log file yet */
2054
        logfp_ = 0;
2055
    }
2056
2057
    /* don't use MORE mode in a log stream */
2058
    int formatter_more_mode() const { return FALSE; }
2059
2060
    /* get my width */
2061
    virtual int get_buffer_maxcol() const { return width_; }
2062
2063
protected:
2064
    /* log streams do not support HTML at the OS level */
2065
    virtual void start_html_in_os() { }
2066
    virtual void end_html_in_os() { }
2067
2068
    /* set the attributes in the underlying stream */
2069
    virtual void set_os_text_attr(int)
2070
    {
2071
        /* log streams are plain text - they don't support attributes */
2072
    }
2073
2074
    /* set the color in the underlying stream */
2075
    virtual void set_os_text_color(os_color_t, os_color_t)
2076
    {
2077
        /* log streams are plain text - they don't support colors */
2078
    }
2079
2080
    /* set the body color */
2081
    virtual void set_os_body_color(os_color_t)
2082
    {
2083
        /* log streams are plain text - they don't support colors */
2084
    }
2085
2086
    /* display text to the underlying OS device */
2087
    virtual void print_to_os(const char *txt)
2088
    {
2089
        /* display the text through the log file */
2090
        if (logfp_ != 0)
2091
            os_fprintz(logfp_, txt);
2092
    }
2093
2094
    /* flush the underlying OS-level rendere */
2095
    virtual void flush_to_os() { }
2096
2097
    /* set the window title in the OS layer - no effect for log streams */
2098
    virtual void set_title_in_os(const char *) { }
2099
2100
    /* open a log file */
2101
    int open_log_file(const char *fname);
2102
2103
    /* set the log file to a file previously opened */
2104
    int set_log_file(const char *fname, osfildef *fp);
2105
    
2106
    /* close the log file */
2107
    int close_log_file();
2108
    
2109
    /* 
2110
     *   Process a <nolog> tag.  Since we're a log stream, we hide the
2111
     *   contents of this tag.  
2112
     */
2113
    virtual void process_nolog_tag(int is_end_tag)
2114
    {
2115
        /* turn hiding on or off as appropriate */
2116
        if (is_end_tag)
2117
            --html_in_ignore_;
2118
        else
2119
            ++html_in_ignore_;
2120
    }
2121
2122
    /*
2123
     *   Process a <log> tag.  Since we're a log stream, we show the contents
2124
     *   of this tag, so we can simply parse and ignore it. 
2125
     */
2126
    virtual void process_log_tag(int /*is_end_tag*/)
2127
    {
2128
    }
2129
2130
    /* my log file handle */
2131
    osfildef *logfp_;
2132
    
2133
    /* log file name */
2134
    char *logfname_;
2135
2136
    /* the maximum width to use for our lines */
2137
    int width_;
2138
};
2139
2140
2141
#endif /* VMCONSOL_H */