Channels ▼
RSS

C/C++

C Programming

Source Code Accompanies This Article. Download It Now.


SEP88: C PROGRAMMING

This month we inaugurate the "C Programming" column project, a running feature in which we will develop a communications program that should take several months to complete. To begin the project, we must consider the program's requirements. In my opinion two procedures are necessary during the development of a computer system: first is the requirements analysis; second is the concept of incremental implementation.

A requirements analysis results in a statement of what the program must do and how it must perform. It is expressed in a language understood by both programmers and users. Without it, you never know when you are done. Such an analysis addresses two areas: the functional requirements and the performance requirements. We will postpone the functional requirements step until later and concentrate on the performance requirements for now.

Incremental implementation means that you build a system a little bit at a time and you deliver it to the user in small increments. Subsequent increments will benefit from the experiences that precede them, not only from your experiences but from those of the user as well. Most government projects ignore this wisdom, preferring to have you automate the entire Western Hemisphere, get it done in short order, and then turn it on all at once. There are exceptions. Jim Towles, a manager of construction and facilities engineers at Kennedy Space Center, knows better and says so. "If you want to eat an elephant, you don't do it in one bite." He won't allow any of those computer folks to shove a big system at him all at once. Jim prefers smaller bites of an elephant.

Our project will take the incremental approach. First we will develop the tools to support the performance requirements. Then we will build the minimum system around those tools. The development and use of that first bite will tell us how to proceed with subsequent ones.

Performance Requirements

The performance of the "C Programming" column program must follow these guidelines:

  • It must be an on-line, interactive PC program (is there any other kind?).
  • It will use pop-down menus.
  • It will have context-sensitive help windows.
  • Data entry will be supported by a general purpose window-oriented entry library.
  • There will be a general-purpose text editor package for all text data entry.
  • The communications portion of the program will assume a Hayes-compatible modem.
  • Xmodem data-transfer protocols will be supported (maybe Kermit as well in a later increment).
  • All window functions (data entry, editor, help, menus) will use a common video window library.
For those who do not know what a video window is, here is a brief description. A video window is a rectangular area of the screen that has a border, a title sometimes, and text displays within its border. Windows pop up on top of one another. When a window pops down (goes away), it disappears, and the video displays--other windows, perhaps--that were under it are visible again.

The Window Library

To begin the C column project, we will start with the window library that supports the rest of the program. This package is at the bottom of our design. We are creating the program from the bottom up in this effort. This is not always the right direction, but we have an advantage here because our requirements fall in line with software that I have published elsewhere. Those of you who have read my books will recognize the similarities. This project will use a subset of that software modified for the needs of this program. The window library will be general enough that you can use it for most other window-oriented applications, but it will lack many of the slick features found in the books and in many commercial window libraries. I have left them out because they are not needed here. We want the most efficient program possible.

The window functions use the Turbo C text-mode console functions to manage window placement and data displays. For this reason you will need Turbo C, Version 1.5 or later.

Listing One, page 112, is window.h, which contains the global function prototypes, some #define statements, window structures, and the configuration information. A word about configuration: Many programs come with an installation program that guides you through selections for your screen colors and such. We are not going to include such a feature because this is a program for programmers, to be built by programmers, and to be used by programmers. Programmers can handle configuration parameters by changing the source code and compiling. To ease that process, l will use #define macros for configuration items wherever possible. You will see a block of such macros at the bottom of Listing One. These macros allow you to configure the program's screen colors. There are eight screen items that can be customized. These will give you a clue as to what is coming in the window package in the coming months. The eight items are: data display screens, data blocks (for example, marked blocks in a text editor), help windows, menus, the menu selector bars, data entry windows, the fields in data entry windows, and error messages. As published here, the configuration uses a pattern of blacks and whites for all items. You can use other colors if you wish. The global symbols for colors are provided in Turbo C's conio.h. The possible global symbols are BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHT GRAY, DARK GRAY, LIGHT BLUE, LIGHT GREEN, LIGHT CYAN, LIGHT RED, LIGHT MAGENTA, YELLOW, and WHITE.

Notice the esoteric prototypes and structure under the comment "internal Turbo C stuff." These provide access to Turbo C's internal video logic and are compatible with Turbo C, Version 1.5. If Borland changes these constructs in a future version, we will need to make adjustments. I have used these constructs to do direct screen reads and writes outside the realm of what is allowed by the text video functions in Turbo C. They free us from assembly language and video retrace status registers. This is highly questionable, nonportable, risky, hacker-mentality programming. You are hereby admonished never to use such practices in your own code. I love it.

Listing Two, page 112, is window.c, the window function library. The window library provides six basic functions to support windows. The window concept provides that the most recently established window is addressed by any subsequent window operations, so that when a window is deleted, the one that was established before it becomes the current window. Here is a description of each of the functions.

establish__window--Call this function to establish and display a window. It expects seven integers in its parameter list. The first four parameters identily the window's screen position in character coordinates relative to one. The four coordinates are left, top, right, and bottom. The largest possible window is, therefore, 1,1,80,25. The next two parameters give the window's foreground (text) and background colors. The color global values named above will work here. The last parameter is TRUE or FALSE to tell the window functions whether or not they should save and restore the video memory under the window. This parameter says whether the window is a pop-up window or not. It is provided as a convenience to save heap space when windows do not need to preserve what they cover.

window__title--This function will display a title in the center of the top border of the current window. Pass it the address of a string that has the title you want displayed.

clear__window--This function clears the current window and re-displays its border. If you use it, you will need to rewrite the title with window__title.

delete__window--This function closes the current window. If another window was established immediately before the current one, that other window becomes current. If the window was established with the last parameter to establish__window set to a TRUE value, then the window is erased and replaced by whatever was under it. Otherwise the window remains visible, although the window functions have no further record of it.

text__window--This function displays lines of text in the current window from an array of character pointers. Each pointer points to a string and the last pointer contains a NULL pointer value. You pass the address of the array (a pointer to a character pointer) to the function. The second parameter is an integer subscript relative to one that tells the function which entry in the array will be displayed in the top line of the window. If the array has more lines than will fit in the window, the display stops when the window is filled.

select__window--This function is used to make menus and data selectors out of windows. It assumes that you have already established the window and written the selections into it by using text window. You pass it an integer relative to one that says which selection is highlighted when the menu is first displayed. The next two integer parameters specity the foreground and background colors of the highlight bar. The last parameter is a function pointer. When the function is called, it displays the menu and lets the user page and scroll up and down with the page and arrow keys. The user may also make a selection by pressing the Enter key. When Enter is pressed, the function returns the number, relative to one, of the selection. If the Esc key is pressed, the function returns zero. If the user presses a key not intended for paging, scrolling, or selecting, the function in the function pointer parameter is called (unless the pointer is NULL, in which case no call is made). This provides for help keys, function key selections, and so on. The menu functions to be added later will use this feature.

Window.c includes several other general-purpose functions that will be used throughout the program and have general application to any other program you might develop with this library. Here are descriptions of those functions:

error__message--This is a utility error message function that displays an error message in a window, beeps, and waits for the user to press the Esc key to acknowledge the error. Pass it a pointer to a null-terminated message.

hidecursor--The Turbo C window functions leave the cursor in the window that is in use. This function uses BIOS to hide the cursor for those times when its display would distract the user's attention.

clear__screen--What else? This function clears the screen.

get__key--This function reads a keyboard character. It translates function keys into the equivalent of their scan code with the most significant bit set. These values correspond to those defined in window.h. The getkey function also watches for a help function key if one is programmed and calls the help function if one is provided. These features will be explained in a later column when we install the context-sensitive help window functions.

scroll__window--This function needs some explanation. It is called to scroll the current window up or down one line. It selects one of two ways to do this on the basis of the __video.snow variable. The Turbo C text library has very nicely determined if the screen will display video snow when direct video reads and writes are done. It makes this decision by testing for an Enhanced Graphics Adaptor, which does not snow, a Monochrome Display Adaptor, which does not, or a Color Graphics Adaptor, which does. It goes further and decides that the Compaq version of the CGA does not snow, which, of course, it does not. The result of its decision is recorded in __video.snow. It will decide that some machines snow when they do not. The Toshiba T1000 laptop is an example. All this is fine, but when we use the text functions of Turbo C to scroll the screen on a snowy machine, the scrolling is too slow. This is because every character that is read or written while the scrolling text is moved must wait for video retrace to occur. So, if we allow Turbo C to scroll for us, performance suffers. For that reason, we use BIOS video services to scroll a snob screen. You might ask why we don't just always do that. Ah, the vagaries of an imperfect world! When BIOS scrolls, it uses an annoying blanking of the screen. This annoyance is preferable to the snow or the slow, so it is accepted as the lesser of three evils.

In the next several months we'll add the help functions, menu manager, data entry screens, and a text editor. After that we'll get into the communications part of the program and its ultimate purpose.

C Crotchet Number 3: How C is Taught

(A reminder: crotchets are things that irk people. See last month's "C Programming" for an explanation and crotchets number 1 and number 2.)

My best friend and companion, Judy, is enrolled in a computer science program at a community college in Virginia. She just completed an introductory class in C. The instructor admitted that he was new to C and gave assignments for the development of a small program. He provided examples of how certain things were to be coded. Here is a fragment.

while (fgets(line[lineno ++], 100, fp));

The class was told that this example is how you read a file of text into memory. Notice particularly the semicolon. Most of us can figure out what's happening here. The while is being used to loop until all the lines of a file are read into an array. So what's wrong with this picture? Plenty.

    1. There is no boundary checking. If the file has more lines than the array, the program will probably crash.

    2. The semicolon is on the wrong line. It should be indented below the while statement to tell the reader that it is an intended null statement rather than the accident it appears to be.

    3. The instructor never explained the notion of an operational statement as a component of a conditional expression within a while, nor did he address the use of the null statement, which is there merely to give the while something to do until its condition is FALSE.

    4. The idea that the statement that reads the file also returns the end-of-file condition and can be tested when it is executed was not explained.

    5. The level at which the class is offered suggests that the students are not ready for such concepts as auto-increments that occur at the same time the subscripting integer is used as a subscript.

    6. The constant 100 is a danger spot. If line is a two-dimensional array of strings, the constant can be coded with the size of operator. At least the constant should be equated to a global symbol for better reading and easier maintenance.

Here is a clearer presentation of the same logic with boundary checking added.

while (lineno <MAXLINES) { rtn = fgets(line[lineno], LENGTH, fp); if (rtn = NULL) break; lineno+ +; }

Sure, it uses several lines where one will do, and sure, few seasoned C programmers really write code like that. This crotchet is not for expert C programmers who only program. They are forever encouraged to use the language to its fullest. This crotchet is for those of you who would teach. C encourages tight and concise expressions and for that we hold it dear. The new C programmer, however, needs to move carefully and slowly into such advanced usage. This freedom of syntax is the chief object of criticism of C by devotees of other languages but ducators do not need to teach it so. Programmers can learn nice, readable statement sequences like the one above and approach the tighter, more elegant side of the language at their own pace. What the students were taught was that the instructor's example would read the text into the array. They never learned why.

I plan to devote a future column to the problems of teaching C.

C Crotchet Number 4: Grousing about Upgrade Fees

I like Turbo C. And someday when I get a new corrected version, I might begin to like QuickC. While doing research for books on these compilers, I spent a lot of time on the CompuServe and BIX on-line services. There is no better way to clear up a compiler or language problem than by using the related forums of these services. Lately a common theme has been running through the discussions, one that wastes connect time and deserves comment. When Borland or another vendor releases a new version of a C compiler, they usually charge a nominal upgrade fee to registered users. Such a fee seems reasonable when you consider the value of a C compiler. Many of you will remember what we used to pay for C compilers that had far fewer features, The common complaint, however, is that the programmers feel ripped off by the fee. They argue that since the upgrade includes the correction of known bugs, the vendor should provide it for free. We all know that those multipage full color ads (do you believe Turboman?) and those thousands of programmer person-hours are not free to the compiler vendors. The revenue to continue promoting and improving the product has to come from somewhere. Yet the grumbles persist. One such forum conversation went on for several days about a $10 upgrade charge.

My pal Bill Chaney gets hot and says such folks are "so tight they wouldn't spend a nickel to see a stink bug eat a bale of hay." Easy, Bill....

The C Programming Library

The book to read this month is C Programmer's Guide to Serial Communications by Joe Campbell (Howard W. Sams & Company, 1987). There are over 650 pages of text and code dealing with the subject of asynchronous serial communications on microcomputers. Campbell presents the subject matter in clear but advanced language. This is no book for beginners. You need to be a C programmer at the very least, and it helps to already have a passing acquaintance with the murky depths of serial communications.

Campbell's writing style is refreshing and makes for good if not light reading. He likes to use words that will send many of us scrambling for Webster's, and he makes no attempt to conceal his contempt for what he considers an inferior design. His treatment of the Xmodem and Kermit data-transmission protocols leave the reader with the impression that in his view the world would be a far better place if Joe Campbell had been allowed to design them. Mostly his criticisms are valid and to the point even when obviously born from the precious perspective of clear hindsight, but if I were Ward Christensen or a few others my ears would be burning. In the preface to the book, Campbell refers to his "humble recognition of the demands of [the subject of data communications]," and that is the last evidence of humility you'll find in this book. An occasional lapse in humility, however, is OK when the apparent arrogance is justified or substantiated by equally apparent intelligence and expert knowledge, and Campbell delivers in this book. His description of Kermit is the first one I've read that didn't require at least a second reading to understand.

The book discusses communications theory, hardware, software, and the C language implementations of these concepts. It is a healthy treatment of these subjects and a necessary addition to the library of those involved in C projects where computers talk to one another through serial interfaces. Recently I downloaded a C source code archive file that purports to implement the Kermit protocol in a Unix environment. l wanted to port the undocumented code to the PC and learn from it. To my chagrin I found that the one source file in the archive is incomplete, abruptly ending in the middle of a function, With the explanations in Campbell's book I can now attempt to provide the missing code. Campbell's book does have C functions that implement the Xmodem protocol, but does not have equivalent code for Kermit. Drat.

Until I read this book I never fully understood why serial input-output and the RS-232 "standard" had been so confused for so many years. When I was consulting in 1978, the microcomputer was new to the business world and plug-and-go appliance computer systems were rare. I earned a substantial part of my living with a break-out box, some cable stock, DB25 connectors, and a soldering iron. Every installation had some new serial printer that needed to be connected to some hacked-together microcomputer and nothing ever fit. In the chapter on RS-232 control Campbell explains that RS-232 was never intended to be used for printer handshaking. How come I never knew that? The original RS-232 intentions notwithstanding, most printers back then could be bought with a serial port. This option allowed users of smart terminals and modems to slave a printer to the auxiliary serial port on their terminal. As a consequence, the designers of early microcomputers put serial printer interfaces in their machines to accept those printers already in use. The Centronics parallel standard for printers was around but few systems used it. Maybe the UARTS and line driver chips were cheaper or more available than parallel drivers. Whatever the reason for the neglect of the Centronics interface, when IBM adopted it for the PC they started a trend that eventually sent me happily out of the 1200 baud connectivity business. I could have used Campbell's book back then.

Campbell earns my respect for his acknowledgment of Cole Porter and Johnny Mercer, two popular music composers from the pre-MTV era. They certainly weren't programmers and probably never heard of C, RS-232, or even MIDI, but their legacy to our culture is appreciated among those of us who dawdle too long and too late in the silent ones and zeros.

Next month we will add some features to the window library, look at another book, and kick some more crotchets around. Until then, stay fit and keep coding.

_C PROGRAMMING_ by Al Stevens

[LISTING ONE]

<a name="01a5_000a">

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

void establish_window(int,int,int,int,int,int,int);
void window_title(char *);
void clear_window(void);
void delete_window(void);
void scroll_window(int);
void text_window(char **, int);
int select_window(int, int, int, int (*func)(int,int));
int getkey(void);
void hidecursor(void);
void set_cursor_type(unsigned);
void clear_screen(void);
void writeline(int, int, char *);
void current_window(void);
void error_message(char *);

#define MAX_WINDOWS 10      /* maximum windows open at once */

#define TRUE        1
#define FALSE       0
#define ERROR      -1

#define BELL        7
#define ESC        27
#define SHIFT_HT  143
#define CTRL_T     20
#define CTRL_B      2
#define CTRL_D      4
#define ALT_D     160
#define ALT_F     161
#define ALT_E     146
#define ALT_O     152
#define ALT_S     159

#define F1        187
#define F2        188
#define F3        189
#define F4        190
#define F5        191
#define F6        192
#define F7        193
#define F8        194
#define F9        195
#define F10       196
#define ALT_F7    238

#define HOME      199
#define UP        200
#define PGUP      201
#define BS        203
#define FWD       205
#define END       207
#define DN        208
#define PGDN      209
#define INS       210
#define DEL       211

#define CTRL_HOME 247
#define CTRL_BS   243
#define CTRL_FWD  244
#define CTRL_END  245

/* --------- window definition structure ------------ */
struct wn {
   int lf,tp,rt,bt;        /* window position */
   int ht,wd;              /* window dimensions */
   int wx, wy;             /* window cursor */
   int wtop;               /* top text line */
   int wlines;             /* total text lines */
   int fg,bg;              /* window colors */
   char *wsave;            /* video memory save buffer */
   char **wtext;           /* pointer to text */
};

/* ------ internal Turbo C stuff ------- */
void far * pascal __vptr(int, int);
void pascal __vram(void far *, void far *, int);
extern struct {
   char filler1[4];
   char attribute;
   char filler2[5];
   char snow;
} _video;

/* ------------ window colors --------------- */
#define TEXTFG  WHITE   /* data display screen */
#define TEXTBG  BLACK
#define BLOCKFG BLACK   /* data blocks */
#define BLOCKBG WHITE
#define HELPBG  WHITE   /* help windows */
#define HELPFG  BLACK
#define MENUBG  WHITE   /* menus */
#define MENUFG  BLACK
#define SELECTBG BLACK   /* menu selector bars */
#define SELECTFG WHITE
#define ENTRYFG WHITE    /* data entry windows */
#define ENTRYBG BLACK
#define FIELDFG BLACK    /* data entry fields  */
#define FIELDBG WHITE
#define ERRORFG BLACK   /* error messages */
#define ERRORBG WHITE



<a name="01a5_000b"><a name="01a5_000b">
<a name="01a5_000c">
[LISTING TWO]
<a name="01a5_000c">

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

#include <stdio.h>
#include <alloc.h>
#include <string.h>
#include <conio.h>
#include <mem.h>
#include <dos.h>
#include <stdlib.h>
#include "window.h"

/* --------- window border characters ---------------- */
#define NW   '\332'
#define NE   '\277'
#define SE   '\331'
#define SW   '\300'
#define SIDE '\263'
#define LINE '\304'

int editing;
static union REGS rg;

/* --------- window definition structure ------------ */
struct wn wdo [MAX_WINDOWS];
int curr_wnd;   /* current window */
struct wn wkw;   /* a working window structure */

static void upline(void);
static void downline(void);
static void firstline(void);
static void lastline(void);
static void dline(int, int, int);

/* ----------- establish a new window -------------- */
void establish_window(left,top,right,bottom,foreg,backg,save)
{
   if (curr_wnd < MAX_WINDOWS)   {
      if (curr_wnd)
         wdo[curr_wnd-1] = wkw;
      setmem(&wkw, sizeof(wkw), 0);
      wkw.lf = left;
      wkw.tp = top;
      wkw.rt = right;
      wkw.bt = bottom;
      wkw.fg = foreg;
      wkw.bg = backg;
      wkw.wd = right+1-left;
      wkw.ht = bottom-top-1;
      if (save)   {
         if ((wkw.wsave=malloc((wkw.ht+2)*wkw.wd*2)) == NULL)
            return;
         gettext(left, top, right, bottom, wkw.wsave);
      }
      wdo[curr_wnd++] = wkw;
      current_window();
      clear_window();
   }
}

/* ------- initialize the working window as current -------- */
void current_window()
{
   window(wkw.lf,wkw.tp,wkw.rt,wkw.bt);
   hidecursor();
   if (wkw.fg || wkw.bg)   {
      textcolor(wkw.fg);
      textbackground(wkw.bg);
    }
}

/* ----------- set a window's title -------------- */
void window_title(char *ttl)
{
   writeline((wkw.wd-strlen(ttl)) / 2, 1, ttl);
}

/* ------------ remove a window ------------------ */
void delete_window()
{
   if (curr_wnd)   {
      if (wkw.wsave)   {
         puttext(wkw.lf,wkw.tp,wkw.rt,wkw.bt,wkw.wsave);
         free(wkw.wsave);
      }
      setmem(wdo+curr_wnd-1, sizeof (struct wn), 0);
      --curr_wnd;
      if (curr_wnd)   {
         wkw = wdo[curr_wnd-1];
         current_window();
      }
   }
}

/* ---- clear the window area and display the border ----- */
void clear_window()
{
   int height, width, y = 1;
   char line1[81], line2[81];

   height = wkw.ht;
   width = wkw.wd;
   setmem(line1 + 1, width-1, LINE);
   setmem(line2 + 1, width-1, ' ');
   *line1 = NW;
   line1[width-1] = NE;
   line1[width] = '\0';
   *line2 = SIDE;
   line2[width-1] = SIDE;
   line2[width] = '\0';
   line1[width] = line2[width] = '\0';
   writeline(1, y++, line1);
   while (height--)
      writeline(1, y++, line2);
   *line1 = SW;
   line1[width-1] = SE;
   writeline(1, y, line1);
}

/* --------- scroll the window. d: 1 = up, 0 = dn ---------- */
void scroll_window(d)
{
   if (_video.snow == 0)
      movetext(wkw.lf+1, wkw.tp+1+d,
           wkw.rt-1, wkw.bt-2+d,
           wkw.lf+1, wkw.tp+2-d);
   else   {
      rg.h.ah = d ? 6 : 7;
      rg.h.al = 1;
      rg.h.bh = _video.attribute;
      rg.h.cl = wkw.lf;
      rg.h.ch = wkw.tp;
      rg.h.dl = wkw.rt-2;
      rg.h.dh = wkw.bt-2;
      int86(16, &rg, &rg);
   }
}

/* ---------- display text in a window --------------- */
void text_window(char *txt[], int ln)
{
   int height = wkw.ht;

   wkw.wtext = txt;
   wkw.wtop = ln;
   wkw.wy = 1;
   while (height-- && txt[ln-1])
      dline(ln++, wkw.fg, wkw.bg);
   wkw.wlines = 0;
   while (*txt++)
      wkw.wlines++;
}

static int lineno;
/* -------- page and scroll through a window of text -------- */
int
select_window(int ln,int foreg,int backg,int (*func)(int,int))
{
   int c = 0;
   int frtn;
   int height, dln, ptop;

   if (ln > wkw.wtop + wkw.ht-1 || ln < wkw.wtop)
      text_window(wkw.wtext, ln);
   else
      wkw.wy = ln - wkw.wtop + 1;
   while (TRUE)   {
      lineno = wkw.wtop + wkw.wy - 1;
      ptop = wkw.wtop;
      dline(lineno, foreg, backg);
      if (wkw.wx == 0)
         hidecursor();
      else
         gotoxy(wkw.wx, wkw.wy+1);
      c = getkey();
      if (c == '\r' || c == ESC)
         break;
      switch (c)   {
         case CTRL_HOME:
            firstline();
            break;
         case CTRL_END:
            lastline();
            break;
         case PGUP:
            wkw.wtop -= wkw.ht;
            if (wkw.wtop < 1)
               wkw.wtop = 1;
            break;
         case PGDN:
            wkw.wtop += wkw.ht;
            if (wkw.wtop > wkw.wlines - (wkw.ht-1))   {
               wkw.wtop = wkw.wlines - (wkw.ht-1);
               if (wkw.wtop < 1)
                  wkw.wtop = 1;
            }
            break;
         case UP:
            upline();
            break;
         case DN:
            downline();
            break;
         default:
            if (!editing && wkw.wlines <= wkw.ht)   {
               if (c == HOME)   {
                  firstline();
                  break;
               }
               if (c == END)   {
                  lastline();
                  break;
               }
            }
            if (func)   {
               frtn = (*func)(c, lineno);
               if (frtn == ERROR)
                  putch(BELL);
               else if (frtn)   {
                  wkw.wy = frtn;
                  return frtn;
               }
               c = 0;
            }
            break;
      }
      switch (c)   {
         case HOME:
         case CTRL_HOME:
         case END:
         case CTRL_END:
         case PGUP:
         case PGDN:  if (wkw.wtop != ptop)   {
                     height = wkw.ht;
                     dln = wkw.wtop;
                     while (height-- && wkw.wtext[dln-1])
                        dline(dln++, wkw.fg, wkw.bg);
                     break;
                  }
         default:    dline(lineno, wkw.fg, wkw.bg);
                     break;
      }
   }
   return c == ESC ? 0 : lineno;
}

/* ---------- move up one line --------------- */
static void upline()
{
   if (lineno > 1)   {
      if (wkw.wy == 1)   {
         if (wkw.wtop > 1)   {
            --wkw.wtop;
            scroll_window(0);
         }
      }
      else
         --wkw.wy;
   }
   else if (wkw.wlines <= wkw.ht)
      lastline();
}

/* ----------- move down one line ------------- */
static void downline()
{
   if (lineno < wkw.wlines)   {
      if (wkw.wy == wkw.ht)   {
         scroll_window(1);
         wkw.wtop++;
      }
      else
         wkw.wy++;
   }
   else if (wkw.wlines <= wkw.ht)
      firstline();
}

/* -------- move to the first line --------- */
static void firstline()
{
   wkw.wtop = wkw.wy = 1;
}

/* -------- move to the last line --------- */
static void lastline()
{
   wkw.wtop = wkw.wlines - (wkw.ht-1);
   if (wkw.wtop < 1)
      wkw.wtop = 1;
   wkw.wy = wkw.ht;
   if (wkw.wy > wkw.wlines)
      wkw.wy = wkw.wlines;
}

char spaces[80] =
"                                                                               ";
/* ------- display a line of text, highlight or normal ------ */
static void dline(ln, foreg, backg)
{
   if (foreg || backg)   {
      textcolor(foreg);
      textbackground(backg);
      --ln;
      writeline(2, ln-wkw.wtop+3, *(wkw.wtext + ln));
      if (strlen(*(wkw.wtext + ln)) < wkw.wd-2)
         writeline(2+strlen(*(wkw.wtext + ln)),
                 ln-wkw.wtop+3,
                 spaces + 79 - wkw.wd +
                 strlen(*(wkw.wtext + ln)) + 2 );
   }
}

/* --------- write a line of text to video window ----------- */
void writeline(int x, int y, char *str)
{
   int cl[80], *cp = cl;

   while (*str)
      *cp++ = (*str++ & 255) | (_video.attribute << 8);
      __vram(__vptr(x+wkw.lf-1,y+wkw.tp-1),MK_FP(_DS,cl),cp-cl);
}

/* ------- use BIOS to hide the cursor ---------- */
void hidecursor()
{
   rg.h.ah = 2;
   rg.x.dx = 0x1900;
   rg.h.bh = 0;
   int86(0x10, &rg, &rg);
}

/* ----------- use BIOS to set the cursor type -------------- */
void set_cursor_type(unsigned t)
{
   rg.x.ax = 0x0100;
   rg.x.bx = 0;
   rg.x.cx = t;
   int86(0x10, &rg, &rg);
}

/* ----------- use BIOS to clear the screen ---------------- */
void clear_screen()
{
   window(1,1,80,25);
   gotoxy(1,1);
   rg.h.al = ' ';
   rg.h.ah = 9;
   rg.x.bx = 7;
   rg.x.cx = 2000;
   int86(0x10, &rg, &rg);
}

void (*helpfunc)(void);
int helpkey;

/* ------------- read the keyboard ---------------- */
int getkey()
{
   int c;

   if ((c = getch()) == 0)
      c = getch() | 128;
   if (c == helpkey && helpfunc)   {
      (*helpfunc)();
      c = getkey();
   }
   return c;
}

/* ------- write an error message ------------- */
void error_message(char *ermsg)
{
   int lf = (80-strlen(ermsg)+2)/2;
   int rt = lf+max(strlen(ermsg)+2,15);
   establish_window(lf, 11, rt, 14, ERRORFG, ERRORBG, TRUE);
   gotoxy(2,2);
   cputs(ermsg);
   gotoxy(2,3);
   cputs("(Press [Esc])");
   hidecursor();
   do
      putch(BELL);
   while (getkey() != ESC);
   delete_window();
}












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.
 

Video