Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C++: Of Books, Compilers, and a Window Object


SEP89: C PROGRAMMING

Last month I looked at Bjorne Stroustrup's The C++ Programming Language and decided to find another book to be an introduction to C++. The Stroustrup book, which I will hereafter affectionately call BS (in the tradition of K&R), will serve as a reference once I understand the C++ language better. But because I still needed a C++ tutorial text, I scoured the bookstores.

I found a book that I can recommend for learning C++ but only with strict reservations. The book is The Waite Group's C++ Programming by John Berry and is for C programmers who want to learn C++. Berry is a good writer and a good teacher. The organization and presentation of the material is just right for teaching the extensions that C++ brings to C.

The C++ Programming Language is not, however, for programmers who do not already know C. While Berry writes effectively about C++, he does not attempt to teach ancestral C, so if you don't already know it, you aren't going to learn it here. That omission is understandable and even appropriate -- there are plenty of good texts on C already. But this book has one unforgivable problem, one that most programmers will readily identify with and some will condemn. The code is replete with errors. Most of the errors are ones that the C programmer will spot right away. But someone else might assume that the code works, might assume that C works that way, and might actually try to get the examples to work, an exercise that the author himself should have undertaken before going to print. Some of the programs in this book would not compile, much less execute. Others are built with conventions that might work in the limited contexts of the examples, but that are generally recognized by seasoned C programmers to be undesirable coding practices.

Following are examples of the errors I found in the book. I am about half way through now and can only assume that the second half is no better than the first.

First, the author insists on putting executable code into header files. The typical .h file contains class definitions and their member functions. Programs that have several source code files all using the same classes will run into link problems with multiply-defined functions. C programmers long ago established a convention of style where header files are for definitions and prototypes only, while all statements that reserve or use memory go into .c files. As a newcomer to C++ I am assuming that C++ will have the same convention. If it doesn't, it should.

Next, there are several programs in the book that use the length of a string, as returned by strlen, to allocate a new buffer. Then strcpy copies the string to the buffer. Every C programmer knows that strlen does not count the null terminator character but that strcpy copies it. The examples might work, but only when nothing critical is on the heap just past the new buffer.

The book uses a stack data structure to illustrate C++ class definitions. The stack functions do not use the first element in the stack array, but they do use the element one past the last one. Once again, the example program probably runs OK, but if you use it in a bigger program, you are likely to trash something else. In at least one other place Berry uses a subscript of 2 to address the second element of an array, a technique that does not work in any context. The example code would not deliver the stated results at all. These subscripting errors are typical of those made by new C programmers, particularly programmers coming to C from Basic, and they are the kind of errors that the author would have found if he had taken the time to try out some of his code.

The give_date function allocates a new buffer every time you call it. Nothing ever deletes the buffer. This error occurs several places in the book. Make enough calls to give_date and the heap (called the "free store" in C++) exhausts. BS defines a technique for capturing such errors, but the book ignores it.

The Julian date functions ignore leap years. I suppose that's forgivable, but the book should point it out. Some unsuspecting and trusting programmer might just try to use some of this stuff.

The nth_token function returns the '\0' character when the function is clearly defined to return a character pointer.

Finally, the discussion of function overloading confuses long and int. The examples of operator overloading use strcat improperly.

If you are not already a veteran C programmer, stay away from this book -- it sets bad examples. If you are one, I can recommend it for its textual content. After recognizing the sloppy code and code conventions, I made allowances and proceeded to enjoy the work. It does what I wanted it to do. It clearly explains the C++ extensions in ways that I can understand. It is a shame that the code examples are so bad. They mar an otherwise excellent book.

The book's zeal for C++ clouds its objectivity. In several places it attempts to justify the language and the object-oriented extensions that it brings to C by citing advantages of the new and disadvantages of the old. And in every one of these cases there are adjacent advantages in C and adjacent disadvantages in C++. Perhaps Berry does not understand C as well as he should.

The Worth of the C++ Extensions

As I get further into C++ and the object-oriented paradigm, the real advantage they offer to the C programmer becomes evident. The OOPs proponents might not agree, but I believe that what C++ offers is not a better way to declare and define application-specific objects of information, but a marvelous method for extending the C language with advanced and complex data types.

C and C++ are language environments with no intrinsic input/output operations. Early on, C programmers evolved a standard library of functions that manage file and console I/O. Other standard functions came along as well -- string handlers, math, memory management, and so on. C++, as a superset of ANSI C, inherits all those functions, but it has the potential for another layer of standard extensions to the language. C++ lets you add data types -- objects. This means that you can extend the language to include types that have universal application to programming problems. For example, you might easily define a string object that has most of the functionality of the string operations that Basic programmers have always enjoyed. Other possible object extensions are dollar fields, dates, names, and addresses. These extensions are not complex object trees such as you might build in a custom database application, but common data types that you can define in ways that everyone can use. Besides the obvious ones just mentioned, there could be generic data structures that most programmers use, such as linked lists, stacks, binary trees, AVL trees, B-trees, and many others. C++, far more than C, is extensible.

But beware. A programmer can let his imagination overload his ability, overload the heck out of operators and functions in C++, and the result can be the same kind of incomprehensible code that plagues everything from Cobol to 4GLs.

As C++ overtakes C as the language of choice, we will need C++ standards that go beyond standard functions and type extensions. We need a set of guidelines to use in the design of overloaded operators and functions, lest we all write unreadable code. You could, for example, easily design an object that overloads the plus operator to perform subtraction. That would be a silly thing to do and is an exaggeration of the problem, but C++ offers the potential for some really dense operations, and we need direction in the use of this powerful new tool.

The Zortech C++ Compiler

Last year I began an occasional foray into C++ by using the Zortech C++ compiler. Zortech graciously provided the compiler, which is up to Version 1.07 now. It is a good product, being the descendent of the Datalite C compiler. Zortech C++ includes a full ANSI C compiler as well as the C++ product.

The weaknesses in the Zortech package are in places that do not matter much to me. The ZED editor is a visual joke, but I use Brief. The memory-resident help system is buggy, will freeze your PC, and you should not use it. The MAKE utility program blows up predictably when it finds certain compile errors. The compiler, however, works great. It does not have an integrated development environment after the fashion of Turbo C and QuickC, and it has no debugger. You can compile with certain switches and link with the Microsoft LINK program so that you can use Codeview. Unfortunately, the Turbo Debugger utility program that makes Codeview programs work with TD does not work with Zortech C++ programs, so I am denied the use of my favorite debugger.

I have read that Zortech C++ is not compatible with other C++ language environments, but I do not yet know enough about C++ to judge, other than to say that everything I have learned from BS and the Berry book have worked just fine. Until there are standards for C++ and until the big boys publish C++ compilers, Zortech will do for me.

C++ Windows

To wring out my new C++ skills, I decided to attempt a simple language extension and build a video window data type. Most of my PC programs involve pop-up windows and menus, so the Window data type is a natural jumping-off place.

Listing One is window.h, which defines the Window class and three derived classes. In addition, window.h defines certain global values that are used by these new classes. A C++ program that uses the new classes would include this header file and would link to the object module that is compiled from Listing Two.

Window.h and window.c create a new class, called "Window." Programs can now use objects, which are instances of this class, just as they can use int, float, struct, and the other standard C data types. A Window appears on the screen when it comes into scope and disappears when it leaves scope, so a using program does not open and close the window in the way it would with traditional C programs. This convention imposes a certain amount of structure on programs that use the Window type, not altogether a bad idea.

C++ uses constructor and destructor functions that accompany a new class. The constructor automatically executes when the class comes into scope, and the destructor function executes when the class goes out of scope. The Window's constructor function establishes and displays the window, and the destructor function erases it. When you declare an object as an instance of the class, you include parameters in parentheses that the constructor function uses to construct the object. To declare a Window, you use this format:

  Window wname(lf,tp,rt,bt,fg,bg);

The first four parameters are character coordinates of the four corners of the Window, with the top left corner of the screen being 0,0. The last two parameters are enum color constants as defined in window.h that specify the foreground and background colors of the window.

A class definition specifies the private and public parts of the class. An evolving convention has the data values of the class in the private part and the functions that operate on the class (called "methods" in OOP-speak, "member functions" in C++ terminology) in the public part. Only the class's member functions can read and write the private parts (sorry, but that's what the C++ founders call them) of the class.

The Window's private parts include its colors, screen position, pointers to video memory save buffers, cursor position, and a pointer to a body of text that can be assigned to the Window. The public parts include the constructor and destructor functions, a function to assign an optional title to the Window, overloaded << operators to write characters, lines, and blocks of text to the Window, and functions to manipulate the text in a Window. The comments in window.h tell you what these functions are.

Derived Window Classes

Window.h and window.c contain three classes derived from the Window class. In C++ a derived class inherits the characteristics of the class from which it is derived. The three derived classes provide for Notice Windows, Error Windows, and a YesNo Window. These utility Windows pop up, display a message, wait for a keystroke, and pop down. In each case the Window displays the initializing message that is specified when the class is declared and waits for a keystroke before it goes away. The YesNo Window requires a Y or N keystroke and returns a true value if the key is the Y. The difference between the other two are the Window colors. As published, the Error Window is red with blinking yellow letters, and the Notice Window has black letters on a cyan background. YesNo Windows are white on green.

Console Functions

As with all such systems, we must deal with the console hardware. I have defined a group of functions that manage the screen displays, cursor, and the keyboard. Most of them have corresponding functions in the Zortech display library. Listing Three , console.h contains macros for the Zortech functions and prototypes for the others. Listing Four is console.c, which contains the functions that we need but that are not represented by Zortech display functions. Users of other compilers must substitute macros or functions for those supported by Zortech functions. Following are the console management functions needed by the Window class and provided either as C functions in console.c or macros to Zortech functions in console.h:

hidecursor -- This function hides the cursor so that its position still affects text displays but the cursor itself is invisible.

unhidecursor -- This function makes the cursor visible again.

savecursor -- This function saves the cursor's current configuration on a stack.

restorecursor -- This function restores the cursor's configuration to the one most recently pushed on the savecursor stack.

getkey -- This function gets a keystroke by avoiding DOS calls to avoid Ctrl-Break interrupts on MS-DOS computers. It also translates function keypresses into unique 8-bit values as defined in console.h.

initconsole and closeconsole -- These functions initialize and close the console functions. Many video packages require such functions for setting up system parameters and flushing buffers.

savevideo and restorevideo -- These functions transfer video memory characters to and from a save buffer. They are used to save and restore what a window covers when it comes into scope. The function parameters specify the buffer address and the window's character corner coordinates.

box -- This function draws a single-line box on the screen. The Window class uses it to make a window border.

colors -- This function establishes the foreground and background colors for subsequent screen displays.

setcursor -- This function positions the cursor to the specified character coordinates.

window_printf and window_putc -- These are window-oriented versions of printf and putchar.

videoscroll -- This is a window scrolling function. Its parameters include the direction and number of lines to scroll and the character corner coordinates.

Look.c

The program in Listing Five (page xx) is look.c. It demonstrates the Window classes with a procedure that lets you view a text file in a full-screen Window. Look.c begins by calling the set_new_handler function, a BS technique supported by Zortech that calls your function get when the new operator cannot get any more memory from the free store. Next, the program declares a Window that occupies the entire screen and uses the title method to write a title in the Window's top border. The program sets up a filebuf object, opens a file by using the name on the command line, and associates an instream object with the filebuf.

The C++ stream convention for files is one that I find less than intuitive. Buffers are objects and streams are objects. You declare a buffer and tell it to open a file. Then you declare a stream and associate it with the buffer. To read and write the file, you send messages to the methods of the stream. It seems to me that all that could be done with one object making it simpler and easier to understand. But because the two-object convolution is a BS technique, compiler vendors will, no doubt, perpetuate it forevermore in the name of conformity.

The look.c program reads all the lines of text from the file, puts each line in a new buffer, and puts the addresses of the buffers into an array of character pointers. Then the text goes to the Window by way of the overloaded << operator.

To demonstrate the use of the YesNo derived class, the program uses a YesNo window to ask if you want to continue. If so, the program calls the page member function to let you scroll and page through the text.

The program uses Error objects to tell you that there was no file name on the command line or that it cannot find the file that you specified.

A Long Way Up the C++ Slope

The learning curve I climbed to get these window extensions working was steep indeed, and it was made worse by the dearth of good OOPs and C++ tutorial literature. Things would have been smoother if the Berry book had been available for the C++ introduction, but what was really missing was a decent explanation of the OOP paradigm. To fill that gap for you I am about to break tradition for the "C Programming" column and recommend that you beg, borrow, or buy Turbo Pascal 5.5. It does not matter to me if you never use the software; the little book that explains the object-oriented extensions to TP is what you want. It is head and shoulders above any introduction to OOPs I have seen so far.

Next month we'll use the Window class as a base from which to derive some menu classes, thus getting deeper into the inheritance features of C++.

Availability

All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063; or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue and format (MS-DOS, Macintosh, Kaypro).

_C PROGRAMMING COLUMN_ by Al Stevens

[LISTING ONE]

<a name="01ea_000d">



// -------------- window.h

#ifndef WINDOWS
#define WINDOWS

// ---------- screen dimensions
#define SCREENWIDTH 80
#define SCREENHEIGHT 25

// --------- atrribute values for colors
enum color {
    BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY,
    GRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED,
    LIGHTMAGENTA, YELLOW, WHITE, BLINK = 128
};

// ------------ spaces per tab stop (text displays)
#define TABS 4
// ------------ color assignments for window types
#define YESNOFG  WHITE
#define YESNOBG  GREEN
#define NOTICEFG BLACK
#define NOTICEBG CYAN
#define ERRORFG  (YELLOW | BLINK)
#define ERRORBG  RED

// ------------ a video window
class Window {
    unsigned bg, fg;        // window colors
    unsigned lf,tp,rt,bt;   // window position
    unsigned *wsave;        // video memory save buffer
    unsigned *hsave;        // hide window save buffer
    unsigned row, col;      // current cursor row and column
    int tabs;               // tab stops, this window
    char **text;            // window text content
public:
    Window(unsigned left, unsigned top,
           unsigned right, unsigned bottom,
           color wfg, color wbg);
    ~Window(void);
    void title(char *ttl);
    Window& operator<<(char **btext);
    Window& operator<<(char *ltext);
    Window& operator<<(char ch);
    void cursor(unsigned x, unsigned y);
    void cursor(unsigned *x, unsigned *y)
        { *y = row, *x = col; }
    void clear_window(void);
    void clreos(void);          // clear to end of screen
    void clreol(void);          // clear to end of line
    void hidewindow(void);      // hide an in-scope window
    void restorewindow(void);   // unhide a hidden window
    void page(void);            // page through the text
    void scroll(int d);         // scroll the window up, down
    void set_colors(int cfg, int cbg)   // change the colors
        { fg = cfg, bg = cbg; }
    void set_tabs(int t)        // change the tab stops
        { if (t > 1 && t < 8) tabs = t; }
};

// ---------- utility notice window
class Notice : Window   {
public:
    Notice(char *text);
    ~Notice(){}
};

// ---------- utility yes/no window
class YesNo : Window    {
public:
    YesNo(char *text);
    ~YesNo(){}
    int answer;
};

// ---------- utility error window
class Error : Window    {
public:
    Error(char *text);
    ~Error(){}
};

#define max(x,y) (((x) > (y)) ? (x) : (y))
#define min(x,y) (((x) > (y)) ? (y) : (x))

#endif





<a name="01ea_000e"><a name="01ea_000e">
<a name="01ea_000f">
[LISTING TWO]
<a name="01ea_000f">

// -------------- window.c

// A C++ window library

#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include "window.h"
#include "console.h"

#define HEIGHT (bt - tp + 1)
#define WIDTH  (rt - lf + 1)

// ------- constructor for a Window
Window::Window(unsigned left, unsigned top,     // 0 - 79, 0 - 24
              unsigned right, unsigned bottom,
              color wfg, color wbg)
{
    savecursor();
    initconsole();
    hidecursor();
    // ----- adjust for windows beyond the screen dimensions
    if (right > SCREENWIDTH-1)  {
        left -= right-(SCREENWIDTH-1);
        right = SCREENWIDTH-1;
    }
    if (bottom > SCREENHEIGHT-1)    {
        top -= bottom-(SCREENHEIGHT-1);
        bottom = SCREENHEIGHT-1;
    }
    // ------- initialize window dimensions
    lf = left;
    tp = top;
    rt = right;
    bt = bottom;
    // ------- initialize window colors
    fg = wfg;
    bg = wbg;
    // ------- initialize window cursor and tab stops
    row = col = 0;
    tabs = TABS;
    // ---------- save the video rectangle under the new window
    wsave = new unsigned[HEIGHT * WIDTH];
    hsave = NULL;
    savevideo(wsave, tp, lf, bt, rt);
    // --------- draw the window frame
    box(tp, lf, bt, rt, fg, bg);
    // -------- clear the window text area
    clear_window();
    unhidecursor();
}

// ------- destructor for a Window
Window::~Window(void)
{
    // ----- restore the video RAM covered by the window
    restorevideo(wsave, tp, lf, bt, rt);
    delete wsave;
    if (hsave != NULL)
        delete hsave;
    restorecursor();
}

// ------- hide a window without destroying it
void Window::hidewindow(void)
{
    if (hsave == NULL)  {
        hsave = new unsigned[HEIGHT * WIDTH];
        savevideo(hsave, tp, lf, bt, rt);
        restorevideo(wsave, tp, lf, bt, rt);
    }
}

// --------- restore a hidden window
void Window::restorewindow(void)
{
    if (hsave != NULL)  {
        savevideo(wsave, tp, lf, bt, rt);
        restorevideo(hsave, tp, lf, bt, rt);
        delete hsave;
        hsave = NULL;
        colors(fg,bg);
    }
}

// -------- add a title to a window
void Window::title(char *ttl)
{
    setcursor(lf + (WIDTH - strlen(ttl) - 1) / 2, tp);
    colors(fg, bg);
    window_printf(" %s ", ttl);
    cursor(col, row);
}

// ------- write text body to a window
Window& Window::operator<<(char **btext)
{
    cursor(0, 0);
    text = btext;
    if (*btext != NULL)
        *this << *btext++;
    while (*btext != NULL && row < HEIGHT-3)
        *this << '\n' << *btext++;
}

// -------- write a line of text to a window
Window& Window::operator<<(char *ltext)
{
    while (*ltext && col < WIDTH - 2 && row < HEIGHT - 2)
        *this << *ltext++;
    return *this;
}

// -------- write a character to a window
Window& Window::operator<<(char ch)
{
    cursor(col, row);
    switch (ch) {
        case '\n':
            clreol();
            if (row == HEIGHT-3)
                scroll(1);
            else
                row++;
        case '\r':
            col = 0;
            break;
        case '\b':
            if (col)
                --col;
            break;
        case '\t':
            do
                *this << ' ';
            while (col % tabs);
            break;
        default:
            if (col == WIDTH - 2)
                *this << '\n';
            colors(fg,bg);
            window_putc(ch);
            col++;
            return *this;
    }
    cursor(col, row);
    return *this;
}

// ----- position the window cursor
void Window::cursor(unsigned x, unsigned y)
{
    if (x < WIDTH-2 && y < HEIGHT-2)    {
        setcursor(lf+1+x, tp+1+y);
        row = y;
        col = x;
    }
}

// ------ clear a window to all blamks
void Window::clear_window(void)
{
    cursor(0,0);
    clreos();
}

// --- clear from current cursor position to end of window
void Window::clreos(void)
{
    unsigned rw = row, cl = col;
    clreol();
    col = 0;
    while (++row < HEIGHT-2)
        clreol();
    row = rw;
    col = cl;
}

// --- clear from current cursor position to end of line
void Window::clreol(void)
{
    unsigned cl = col;
    colors(fg,bg);
    while (col < WIDTH-2)
        *this << ' ';
    col = cl;
}

// ----- page and scroll through the text file
void Window::page(void)
{
    int c = 0, lines = 0;
    char **tx = text;

    hidecursor();
    // ------ count the lines of text
    while (*(tx + lines) != NULL)
        lines++;
    while (c != ESC)    {
        c = getkey();
        char **htext = text;
        switch (c)  {
            case UP:
                if (tx != text) {
                    --tx;
                    scroll(-1);
                    unsigned x, y;
                    cursor(&x, &y);
                    cursor(0, 0);
                    *this << *tx;
                    cursor(x, y);
                }
                continue;
            case DN:
                if (tx+HEIGHT-3 < text+lines-1) {
                    tx++;
                    scroll(1);
                    unsigned x, y;
                    cursor(&x, &y);
                    cursor(0, HEIGHT-3);
                    *this << *(tx + HEIGHT - 3);
                    cursor(x, y);
                }
                continue;
            case PGUP:
                tx -= HEIGHT-2;
                if (tx < text)
                    tx = text;
                break;
            case PGDN:
                tx += HEIGHT-2;
                if (tx+HEIGHT-3 < text+lines-1)
                    break;
            case END:
                tx = text+lines-(HEIGHT-2);
                if (tx > text)
                    break;
            case HOME:
                tx = text;
                break;
            default:
                continue;
        }
        *this << tx;
        text = htext;
        clreos();
    }
    unhidecursor();
}

// --------- scroll a window
void Window::scroll(int d)
{
    videoscroll(d, tp+1, lf+1, bt-1, rt-1, fg, bg);
}

// ------ utility notice window
Notice::Notice(char *text)
    : ((SCREENWIDTH-(strlen(text)+2)) / 2, 11,
        ((SCREENWIDTH-(strlen(text)+2)) / 2) + strlen(text)+2,
        14, NOTICEFG, NOTICEBG)
{
    *this << text << "\n Any key ...";
    hidecursor();
    getkey();
    unhidecursor();
    hidewindow();
}

// ------ utility error window
Error::Error(char *text)
    : ( (SCREENWIDTH-(strlen(text)+2)) / 2, 11,
        ((SCREENWIDTH-(strlen(text)+2)) / 2) + strlen(text)+2,
        14, ERRORFG, ERRORBG)
{
    *this << text << "\n Any key ...";
    hidecursor();
    getkey();
    unhidecursor();
    hidewindow();
}

// ------ utility yes/no window
YesNo::YesNo(char *text)
    : ( (SCREENWIDTH-(strlen(text)+10)) / 2, 11,
        ((SCREENWIDTH-(strlen(text)+10)) / 2) + strlen(text)+10,
        13, YESNOFG, YESNOBG)
{
    *this << text << "? (Y/N) ";
    int c = 0;
    hidecursor();
    while (tolower(c) != 'y' && tolower(c) != 'n')
        c = getkey();
    unhidecursor();
    hidewindow();
    answer = tolower(c) == 'y';
}







<a name="01ea_0010"><a name="01ea_0010">
<a name="01ea_0011">
[LISTING THREE]
<a name="01ea_0011">

/* ----------- console.h -------- */

#ifndef CONSOLE
#define CONSOLE

#include <disp.h>

// -------- cursor and keyboard functions (via BIOS)
void hidecursor(void);
void unhidecursor(void);
void savecursor(void);
void restorecursor(void);
int getkey(void);

// -------- key values returned by getkey()
#define BELL      7
#define ESC      27
#define UP      200
#define BS      203
#define FWD     205
#define DN      208
#define HOME    199
#define END     207
#define PGUP    201
#define PGDN    209

#define attr(fg,bg) ((fg)+(((bg)&7)<<4))

// --------- video functions (defined as Zortech C++ equivalents)
#define initconsole()            disp_open()
#define closeconsole()           disp_flush()
#define savevideo(bf,t,l,b,r)    disp_peekbox(bf,t,l,b,r)
#define restorevideo(bf,t,l,b,r) disp_pokebox(bf,t,l,b,r)
#define box(t,l,b,r,fg,bg)       disp_box(1,attr(fg,bg),t,l,b,r)
#define colors(fg,bg)            disp_setattr(attr(fg,bg))
#define setcursor(x,y)           disp_move(y,x)
#define window_printf            disp_printf
#define window_putc              disp_putc
#define videoscroll(d,t,l,b,r,fg,bg) \
        disp_scroll(d,t,l,b,r,attr(fg,bg));


#endif







<a name="01ea_0012"><a name="01ea_0012">
<a name="01ea_0013">
[LISTING FOUR]
<a name="01ea_0013">

/* ----------- console.c --------- */

/* PC-specific console functions */

#include <dos.h>
#include <conio.h>
#include "console.h"

/* ------- video BIOS (0x10) functions --------- */
#define VIDEO         0x10
#define SETCURSORTYPE 1
#define SETCURSOR     2
#define READCURSOR    3
#define HIDECURSOR    0x20

#define SAVEDEPTH     20  /* depth to which cursors are saved */

static int cursorpos[SAVEDEPTH];
static int cursorshape[SAVEDEPTH];
static int sd;

union REGS rg;

/* ---- Low-level get cursor shape and position ---- */
static void getcursor(void)
{
    rg.h.ah = READCURSOR;
    rg.h.bh = 0;
    int86(VIDEO,&rg,&rg);
}

/* ---- Save the current cursor configuration ---- */
void savecursor(void)
{
    getcursor();
    if (sd < SAVEDEPTH) {
        cursorshape[sd] = rg.x.cx;
        cursorpos[sd++] = rg.x.dx;
    }
}

/* ---- Restore the saved cursor configuration ---- */
void restorecursor(void)
{
    if (sd) {
        rg.h.ah = SETCURSOR;
        rg.h.bh = 0;
        rg.x.dx = cursorpos[--sd];
        int86(VIDEO,&rg,&rg);
        rg.h.ah = SETCURSORTYPE;
        rg.x.cx = cursorshape[sd];
        int86(VIDEO,&rg,&rg);
    }
}

/* ---- Hide the cursor ---- */
void hidecursor(void)
{
    getcursor();
    rg.h.ch |= HIDECURSOR;
    rg.h.ah = SETCURSORTYPE;
    int86(VIDEO,&rg,&rg);
}

/* ---- Unhide the cursor ---- */
void unhidecursor(void)
{
    getcursor();
    rg.h.ch &= ~HIDECURSOR;
    rg.h.ah = SETCURSORTYPE;
    int86(VIDEO,&rg,&rg);
}

/* ---- Read a keystroke ---- */
int getkey(void)
{
    rg.h.ah = 0;
    int86(0x16,&rg,&rg);
    if (rg.h.al == 0)
        return (rg.h.ah | 0x80) & 255;
    return rg.h.al & 255;
}






<a name="01ea_0014"><a name="01ea_0014">
<a name="01ea_0015">
[LISTING FIVE]
<a name="01ea_0015">

// ---------- look.c

// A C++ program to demonstrate the use of the window library.
// This program lets you view a text file

#include <stdio.h>
#include <string.h>
#include <stream.hpp>
#include <stdlib.h>
#include "window.h"

#define MAXLINES 200            // maximum number of text lines

static char *wtext[MAXLINES+1]; // pointers to text lines

// --- taken from BS; handles all free store (heap) exhaustions
void out_of_store(void);
typedef void (*PF)();
extern PF set_new_handler(PF);

main(int argc, char *argv[])
{
    set_new_handler(&out_of_store);
    if (argc > 1)   {
        // ---- open a full-screen window
        Window wnd(0,0,79,24,CYAN,BLUE);
        char ttl[80];
        // ------ put the file name in the title
        sprintf(ttl, "Viewing %s", argv[1]);
        wnd.title(ttl);
        filebuf buf;
        if (buf.open(argv[1], input))   {
            istream infile(&buf);
            int t = 0;
            // --- read the file and load the pointer array
            char bf[120], *cp = bf;
            while (t < MAXLINES && !infile.eof())   {
                infile.get(*cp);
                if (*cp != '\r')    {
                    if (*cp == '\n')    {
                        *cp = '\0';
                        wtext[t] = new char [strlen(bf)+1];
                        strcpy(wtext[t++], bf);
                        cp = bf;
                    }
                    else
                        cp++;
                }
            }
            wtext[t] = NULL;
            // ---- write all the text to the window
            wnd << wtext;
            // ---- a YesNo window
            YesNo yn("Continue");
            if (yn.answer)
                wnd.page();
            // ------ a Notice window
            Notice nt("All done.");
        }
        else
            // ------ error windows
            Error err("No such file");
    }
    else
        Error err("No file name specified");
}

// ----- the BS free-store exhaustion handler
void out_of_store(void)
{
    cerr << "operator new failed: out of store\n";
    exit(1);
}












Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.