C PROGRAMMING

Al discusses menu structuring, file management, and text searching along with an implementation of a context-sensitive help facility.


December 01, 1988
URL:http://www.drdobbs.com/cpp/c-programming/184408046

DEC88: C PROGRAMMING

Over the last several months, we shave been assembling a library of window-oriented tools to be used in the development of a communications program. Last month's column featured a text editor engine that allows you to collect user-entered, free-form text into a buffer through a window. This month we will surround that engine with a menu structure and add file-management and text-searching functions. First, though, we must complete the collection of window tools by adding the context-sensitive help feature.

Most PC programs offer some type of on-line help to the user. This help usually takes the form of text windows that pop up and explain something the user needs to know. When the text automatically reflects the user's current position in the program, the help text is said to be "context-sensitive."

The context-sensitive help feature for our C Programming project is implemented in two source files. These are Listing One, help.h, on page112, and Listing Two, help.c, on page 112. The code from previous months has included constructs to support the help functions. The data entry and menu functions in October provide for the identification of help windows to explain each menu selection and data entry field. The structures found in entry. h and menu.h already have places for the window mnemonics, and the getkeyfunction (window.c, September DDJ already watches for a help hot key and calls a help function.

Help windows are described in an ASCII text file. Each window has a mnemonic and text. The mnemonic is from 1 to 8 characters, and the text can be up to 78 characters wide and 23 lines deep. Each mnemonic is surrounded by angle brackets, appears at the beginning of a line, and is the only entry. on the line. The text for a window follows its mnemonic and is terminated by the appearance of the next window mnemonic or the <end> file-terminating mnemonic. Listing Three, page 121, is twrp.hlp, a help text file that we will use in this month's expansion of the editor. Look at this listing now for an understanding of the idea. Later you might want to change the file to reflect changes you make to the editor commands when you customize the editor with your own preferred command set.

The getkey function watches two global variables -the function pointer named helpfunc and the integer named helpkey. If a program wants to intercept a help keystroke, it can assign a function address and a keystroke value to these variables. Then, if getkey senses that the user has pressed the assigned help key, it calls the assigned function.

The source file named help.c includes the function load_help, which an application program calls to load a help text file. The function assigns a help function key (F1 in our implementation) by initializing the helpkey integer and assigns its own help function (display_help) by initializing the helpfunc pointer. The file help.h contains a macro, set that allows an application function to name the mnemonic for a help window. With this macro, an application can maintain the proper current window for the help context of the program. The source files entry.h and menu.h from October define structures that an application program initializes to use the data entry and menu functions. The structures include pointers to character pointers, which, if initialized, provide the window mnemonics for help windows for menu selections and data entry fields. The display_help function senses that an application has used set-help to establish a help window, that the menu cursor is on a menu selection, or that a data entry field is being processed. The display_help function thus selects the appropriate window if the user presses the help key. With this help facility applied along with the other window functions, the applications programmer can employ context-sensitive help windows for menus, data entry screens, and other program modes. The system developer needs only to do the following things to implement the feature:

    1. Build a help text file.

    2. Insert window mnemonics into the menu and data entry structures.

    3. Insert set-help macros into the application program for help windows that are not related to menus or data entry fields.

The Tiny Word Processor

I promised you an expanded text editor, one that will support the communications program. For this editor, we will use the window editor function from November, the data entry and menu functions from October, and the context-sensitive help window functions just described. We can call this expanded editor from applications programs just as we can the less well-endowed editor engine of last month. To provide an example of the expanded editor, I built a simple front end that uses the editor in the way that our utility program will use it --as a tiny word processor. With the front end, the program becomes a stand-alone editor package with minimum wordprocessing features and capabilities.

This new program marks a milestone --I am using it to write this column. This drastic measure is a test to ferret out the hidden bugs. It is a risky endeavor because I stand to lose my work if this unproven editor crashes or fails to save my deathless prose correctly. For this reason I will be making lots of departures from typing to save the text and take a look at what was saved --the sanctity of my sanity included. This new program is the first time we use the tools in an actual application. As my own beta tester, I find things that need to be changed. The first modification I make is to the editor colors, making them more pleasing than the bland monochrome combinations in the window.h defaults. These selections are personal; you'll want to make your own.

Next comes a pleasant discovery. Much of my work is done on airplanes, and I use a barebones Toshiba T1000 laptop MS-DOS computer --low cost, low weight, minimum capabilities. As it happens, these words are being written between Dallas and Phoenix. One of the drawbacks to this bottom-of-the-line machine is that it takes forever to load any of the full-functioned word processors from the single 3.5-inch diskette. But our new tiny word processor is a mere 30K and loads itself in a few seconds, thus solving a months old problem. Now I have to deal with another problem. Whenever I haul out the T1000, set it up on the tray table, and get deeply engrossed, my neighbor in the next seat gets interested in the cute little machine. This brings the inevitable tap on the arm and the subsequent unsolicited computer discussion in which I hear about the traveler's office computers and how he knows nothing about them (but his son is a whiz). People always ask where I got the T1000, what it cost, and what I use it for. My pal Bill Chaney would grumble something about keeping breeding records for pit bulls, none of which would be true, but he wouldn't be bothered anymore and could then work undisturbed.

To build the tiny word processor, which I will now call TWRP (pronounced " twerp"), we surround the window text editor from last month with a menu shell and some file-management features. These features employ the menu and data entry functions from October. We want the editor to be available in two ways: as a stand-alone program and as a function that can be called from an application program. TWRP is built from two source files. Listing Four, page 122, is editshel.c. This file contains the menu shell and the advanced editor commands for file management and text searching. With editshel.c, an application program can link to the advanced editor features. Listing Five, page 124, is twrp.c, the outer layer that uses the advanced editor to build TWRP as a stand-alone program.

Notice that twrp.c contains the call to load_help to load the help file for the editor. By isolating this call in the outer function, we reserve the ability for other programs that call the editor to have other, perhaps more complex, help files.

editshel.c contains a set of expanded commands as case statements in a switch statement. The function that contains this switch is named editmenu. This function represents the technique we will use to add features to the editor as they are required. When editor.c encounters a key stroke that it does not recognize, it calls the function addressed in an external function pointer named edit_func. If the pointer is NULL the editor rings the bell to indicate that the command is invalid. By initializing that pointer, we can extend the command set. By putting a similar function pointer chain at the end of our extended command set, we can keep the door open for future extensions. It would be easier to add cases to the original editor's command-dispatching switch, but then we would need to publish a modified editor.c each time we added features to the editor. With this approach each extension is contained in the source module that has the extending code. Another advantage of our extension strategy is that it gives the editor multiple levels of functionality. The current level depends on the context in which the editor is executed. A program that uses the expanded editor can also use the simpler window editor to collect small amounts of text for other reasons. That invocation of the editor would not, for example, have file-management commands because the extension function pointer would be NULL for the duration of the editing session.

Because we are discussing extended editor commands, we should consider what the big-time word processors do that TWRP cannot do. TWRP has no spelling checker or thesaurus, no keyboard macros, no fonts, no columns, no index or table of contents generator, no style sheets, no footnotes. It sounds a lot like the WordStar of yore. But wait, there's more --or less, depending on how you look at it. TWRP has no underscoring, boldface, or subscripting and superscripting; no pagination; no mail-merge; no headers and footers; no printing; no variable margins.

So what good is it, and what does it do? TWRP is primarily a text-composition tool, supporting the most basic text entry activities needed by writers of everything from memos to manuscripts. You type the text in, and TWRP saves it for you. You can load and save text files; merge text from other files; form paragraphs; mark blocks; and move, copy, and delete blocks.

TWRP is a lot like the popular desktop accessory notepad programs. It has, however, one unique and endearing quality --it can be linked into our pro grams as an in-line word processor, fully integrated with whatever else we have going on. What's more, the source code is ours and we can make it do whatever we like.

This observation returns us to the subject of macros. TWRP has no keyboard macros in the style of many editors and word processors. Brief, the world class programmer's editor from Solution Systems, has a macro language that resembles Lisp. The ME editor from Magma Software Systems and its derivative, the New York Word Processor, have macro languages that resemble C. The new Microsoft Editor uses compiled C extensions for advanced macros. Borland's Sprint word processor has a procedural macro language. So does XyWrite from XyQuest. All these editors and word processors have compiled or interpreted macro languages, some of which resemble C.

In that spirit, therefore, TWRP is, by declaration and acclamation, blessed with a macro language that not only resembles C, it is C. If you want to add a macro, you add an extension function in the manner described previously, write the procedure as a C function, and recompile and link TWRP. What better way for a C programmer to add a complex macro to an editor?

The MAKE Files

Listing Six, page 124, is twrp.prj, the Turbo C project make file for the Turbo C environment compiler. Use the compact- memory model with all error checking enabled. You will need source files from the September, October, November, and this month's C Programming column.

C Tool Set Errata

My use of the tools has uncovered a few minor problems that I will address here. When I moved to the compactdata model to support an 800-line edit buffer, a bug showed up in window.c (September DDJ). The writeline function has some nonportable pointer juggling that needs to be tightened up. Substitute these lines for the call to the _vram function:

_vram(_vptr(x+wkw.lf-1, y+wkw.tp-1),

        (int far *) cl,
   (unsigned) ((int far *) cp - (int far *) cl));

The menu select function in menu.c failed to reset the global pointer named in to NULL before returning. Insert this statement before the return from menu select at the bottom of the function:

     mn = NULL;

I will update the download versions of these files on CompuServe to reflect these corrections.

Turbo C2.O

All the window tools from the C Programming columns have been built with Turbo C, Version 2.0. This is a last minute development; 2.0 has not been out long. The big news is the integrated debugger in the Turbo C environment; everyone has been waiting for that well-rumored feature for a long time, and Turbo C has now removed the last reason for feeling inferior to QuickC. I have spent a lot of time using the debugger and can say without reservation that you will be delighted with it. I am especially impressed by its seamless integration with the editor and compiler environment. Our small TWRP project is by no means an exhaustive test of the compiler, so this endorsement is preliminary but enthusiastic.

Turbo C has a new pop-up utility program named THELP that uses the Turbo on-line help file. This is a significant addition to the package. You know how Ctrl-Fl works in the TC environment- it pops up a context-sensitive help window about Turbo C. Now you can have those same language and compiler help displays from within your personal text editor (including TWRP). The THELP hot key pops up the Turbo help windows over whatever you happen to be doing. If the cursor is on a Turbo keyword -a library function name, perhaps -the help window related to that particular word is displayed.

TesSeRact

THELP was built from the TesSeRact shareware memory-resident program library. This library is available in files you can download from CompuServe in the CL and Borland libraries and from other BBSs. Source code is available to registered users.

TesSeRact is said to be the answer to a terminate-and-stay-resident (TSR) programmer's prayer, able to make any program a well-behaved TSR capable of running in harmonious accord with other programs, TSR and otherwise. I don't completely believe this, but if you want to write TSRs without understanding how they work, this is a good way to go. The documentation explains how to use the libraries, which are available for C, assembly language, and Pascal. To use the libraries in programs that you distribute, you must register your use and keep the TesSeRact signature in the code. That's how I figured out that ThELP is a TesSeRact program.

The developers of TesSeRact are experts in the black art of TSR programming. The development team is what remains of the Ringmaster project, a failed industry attempt to settle on a standard for TSRs. Given a lack of enthusiasm among private software publishers to sign up to such a standard, some of the team splintered off and took the shareware approach. The libraries that resulted provide a reasonably safe way to get a TSR program running with a minimum of fuss. Having researched, developed, and written extensively about TSR programs, I am convinced that there is no way to write one that can be guaranteed to work in all PC hardware environments and in the company of all other programs and systems. MS-DOS just wasn't meant to be used that way, and all those successful TSRs are the legacy of a generation of hackers who figured out ways around the limitations of DOS --even if a lot of the programs don't work with one another. TSRs, working or not, will be with us as long as MS-DOS is with us, and that is going to be a long time.

A Crotchet: The Trouble with Programming

The trouble with programming is that it isn't getting any easier. Quite the opposite, programming is getting a lot harder.

In recent months I have been looking at new or different software development environments. In a recent column I told of my first exposure to C++ and object-oriented programming, a most humbling experience. Another venture into the unknown (to me, that is) was my excursion into the Macintosh. Someone observed that it takes a good programmer at least a year to become a competent Mac programmer in C or any other language.

Now I am unfolding the OS/2 Software Development Kit (SDK) from Microsoft. Seventy-five pounds of manuals and diskettes and eight videotapes comprise this formidable beast. At first glance it appears that unraveling what manuals contain what information is itself a monumental effort, not to mention the learning process that follows. I have only just begun, and I am wondering how many blue moons will pass before I see an OS/2 screen group displaying my very own "Hello, world" message.

No, programming isn't getting any easier. Remember the promise of application generators and fourth-generation languages that were going to put system design and programming within the reach of Everyman? A new wave of dBase and NOMAD pseudo-programmers were going to put us all out of business. Not to worry. The complexity of these new software development platforms ensures a place for hackerlevel talent for years to come.

The best part of this gradual evolution to more complicated programming is that we don't need to learn a new language. C is surviving and is still that overworked cliche, the "language of choice" -not that it will be easy to remember the 900+ OS/2 function calls, but at least their basis is in the syntax of the best programming language ever devised.

It does, however, raise my C purist's hackles when I see the function declarations assigned the pascal type in their prototypes. It is fortunate that most of those blights are hidden away in header files. Indeed!

A Book for All Seasons

When you tackle OS/2, plan to spend some money at the bookstore. You will have to augment the 75 pounds of abstruse Microsoft manuals with a few more readable offerings from other authors. I have most of the books that have been written about OS/2, and one stands out as the essential first book to read. Its title is Inside OS/2, and its author is Gordon Letwin. Do not confuse this book with another one with the same title. This one is published by Microsoft Press, and to its credit, Microsoft includes a copy of it with the SDK. Inside OS/2 is not the only book you will need, but it is the first. Read it even before you watch the videotapes. That way, if you sleep through something important, chances are Letwin has already explained it.

Inside OS/2 is an excellent description of the underlying design principles for OS/2. It will help if you already know a little bit about operating system theory. An understanding of the concepts explained in this work is essential for the design of programs that would effectively use the advanced features of OS/2. I like this book.

(The little old lady in the next seat is eyeing my T1000. Soon she'll be tapping me on the arm. Where are you, Bill Chaney, when I need you?)

_C PROGRAMMING COLUMN_ by Al Stevens

[LISTING ONE]



/* ------------ help.h -------------- */

void load_help(char *);
void display_help(void);

extern char *help_window;
#define set_help(s) help_window=s



[LISTING TWO]


/* --------- help.c ----------- */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
#include "window.h"
#include "menu.h"
#include "entry.h"
#include "help.h"

#define MAXHELPS 25

static struct helps {
    char hname [9];
    int h, w;
    long hptr;
} hps [MAXHELPS+1];

extern FIELD *fld;
extern MENU *mn;

static int hp = 0;
static FILE *helpfp;
static char hline [80];
char *help_window;

/* ----------- load the HELP! definition file ------------ */
void load_help(char *hn)
{
    extern void (*helpfunc)(void);
    extern int helpkey;
    char *cp;

    helpfunc = display_help;
    helpkey = F1;
    hp = 0;
    if ((helpfp = fopen(hn, "r")) == NULL)
        return;
    if ((fgets(hline, 80, helpfp)) == NULL)
        return;
    while (1)   {
        if (hp == MAXHELPS)
            break;
        if (strncmp(hline, "<end>", 5) == 0)
            break;
        if (*hline != '<')
            continue;
        hps[hp].h = 2;
        hps[hp].w = 23;
        strncpy(hps[hp].hname, hline+1, 8);
        hps[hp].hname[8] = '\0';
        cp = strchr(hps[hp].hname, '>');
        if (cp)
            *cp = '\0';
        hps[hp].hptr = ftell(helpfp);
        if (fgets(hline, 80, helpfp) == NULL)
            strcpy(hline, "<end>");
        while (hline[0] != '<') {
            hps[hp].h++;
            hps[hp].w = max(hps[hp].w, strlen(hline)+2);
        if (fgets(hline, 80, helpfp) == NULL)
            strcpy(hline, "<end>");
        }
        hp++;
    }
}

/* ---------- display the current help window ----------- */
void display_help()
{
    int hx, hy, i, xx, yy, ch;
    extern int helpkey, hsel;
    char *save_help;
    static int inhelp = 0;
    extern struct wn wkw;

    if (inhelp)
        return;
    inhelp++;
    save_help = help_window;
    if (fld != NULL)
        help_window = fld->fhelp;
    else if (mn != NULL)
        help_window = (mn+hsel-1)->mshelp [wkw.wy-1];
    if (help_window != NULL)    {
        for (ch = 0; ch < hp; ch++)
            if (strcmp(help_window, hps[ch].hname) == 0)
                break;
        if (ch < hp)    {
            xx = wherex();
            yy = wherey();
            hidecursor();
            hx = ((80-hps[ch].w) / 2)+1;
            hy = ((25-hps[ch].h) / 2)+1;
            establish_window(hx, hy,
                    hx+hps[ch].w-1, hy+hps[ch].h,
                    HELPFG, HELPBG, TRUE);
            fseek(helpfp, hps[ch].hptr, 0);
            for (i = 0; i < hps[ch].h-2; i++)   {
                gotoxy(2,2+i);
                fgets(hline, 80, helpfp);
                cprintf(hline);
            }
            gotoxy(2,2+i);
            cprintf(" [Any key to return]");
            hidecursor();
            getkey();
            delete_window();
            if (mn == NULL || fld != NULL)    {
                textcolor(FIELDFG);
                textbackground(FIELDBG);
                gotoxy(xx, yy);
            }
        }
    }
    help_window = save_help;
    --inhelp;
}



[LISTING THREE]


<editor>
              TINY WORD PROCESSOR (TWP) COMMANDS
-------Cursor Movement------   ---------Page Movement--------
arrows    = move text cursor   Ctrl-Home = Beginning of File
Ctrl-T    = Top of Window      Ctrl-End  = End of File
Ctrl-B    = Bottom of Window   PgUp      = Previous Page
Ctrl ->   = Next Word          PgDn      = Next Page
Ctrl <-   = Previous Word
Home      = Beginning of Line  ---------Editor Control-------
End       = End of Line        Alt-A  = Auto Paragraph Reform
Shift-Tab = Back tab

--------Block Controls------   ---------Edit Commands--------
F2  = Form Paragraph           Alt-Q or Esc = Done
F5  = Mark Block Beginning     Ins          = Insert Mode
F6  = Mark Block End           Del          = Delete Char
F3  = Move Block               <--          = Rubout
F4  = Copy Block               Ctrl-D       = Delete Word
F8  = Delete Block             Alt-D        = Delete Line
F9  = Unmark Block             F7           = Find
                               Alt-F7       = Find again
<load>
Load a new file into TWP,
replacing the existing file.
<save>
Save the file from the
edit buffer.
<merge>
Merge a file into the
edit buffer at the
line where the cursor
is pointed.
<new>
Clear the edit buffer
and create a new file.
<quit>
Exit from TWP, returning to DOS
<move>
Move the block to the
line where the cursor
points. This is an
insert move.
<copy>
Copy the block to the
line where the cursor
points. This is an
insert copy.
<delete>
Delete the block closing
the space it occupies.
<hide>
Turn off the block
markers.
<formpara>
Form a paragraph.
This makes a paragraph from
a marked block, or, if no
block is marked, to the next
blank or indented line.
<markbeg>
Mark the beginning line
of a block for move, copy,
delete, or paragraph.
<markend>
Mark the ending line
of a block for move, copy,
delete, or paragraph.
<find>
Find a specified string
in the edit buffer.
Move the cursor to the
location where the string
was found.
<findagn>
Find the next occurrence
of the string most recently
specified.
<auto>
Turn on/off the automatic
paragraph forming feature.
<insert>
Turn on/off the
character insert mode.
(insert/overstrike toggle)
<filename>
Enter the path and file name.
The path is optional but must
be fully qualified if entered.
<findstr>
Enter the string to be searched
by the Find command.
<end>



[LISTING FOUR]


/* --------- editshel.c ------------ */
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <stdlib.h>
#include <mem.h>
#include <alloc.h>
#include <ctype.h>
#include "window.h"
#include "menu.h"
#include "entry.h"
#include "editor.h"
#include "help.h"

int MAXLINES;           /* maximum number of editor lines */
#define EDITWIDTH  78   /* length of an editor line       */
#define BUFLEN (EDITWIDTH*MAXLINES)

/* --------- alt keys returned by getkey() --------- */
#define ALT_A 158
#define ALT_L 166
#define ALT_M 178
#define ALT_N 177

/* -------- configured advanced editor commands --------- */
#define FIND            F7
#define FIND_AGAIN      ALT_F7
#define LOAD_FILE       ALT_L
#define SAVE_FILE       ALT_S
#define MERGE_FILE      ALT_M
#define NEW_FILE        ALT_N
#define EDITOR_MENU     F10
#define REFORM          ALT_A

/* ---------- editor menu tables --------- */
static char *fselcs[] = {
    "Load   [Alt-L]",
    "Save   [Alt-S]",
    "Merge  [Alt-M]",
    "New    [Alt-N]",
    "Quit   [Alt-Q]",
    NULL
};

static char *filehelp[] = {
    "load",
    "save",
    "merge",
    "new",
    "quit"
};

static char *eselcs[] = {
    "Move         [F3]",
    "Copy         [F4]",
    "Delete       [F8]",
    "Hide         [F9]",
    "Paragraph    [F2]",
    "Mark Beg     [F5]",
    "Mark End     [F6]",
    "Find         [F7]",
    "Find Again   [Alt-F7]",
    NULL
};

static char *edithelp[] = {
    "move",
    "copy",
    "delete",
    "hide",
    "formpara",
    "markbeg",
    "markend",
    "find",
    "findagn",
};

static char *oselcs[] = {
    "Auto Paragraph Reformat   [Alt-A]",
    "Insert                    [Ins]",
    NULL
};

static char *opthelp[] = {
    "auto",
    "insert"
};

void fileedit(char *);
static int  edit(int,int);
static void editmenu(int);
static int  get_filename(char *, int);
static int  write_file(void);
static int  load_file(int,int);
static int  save_file(int,int);
static int  merge_file(int,int);
static int  new_file(int,int);
static void editkeys(int);
static void statusline(void);
static int  findstring(void);
static char *find(char *, unsigned);
static int  read_file(char *, char *, int, int, int);
static int  bufferok(char *);
static void notice(char *);
void (*edit_extend)(void);

static char fkeys[] =   {LOAD_FILE,SAVE_FILE,
                         MERGE_FILE,NEW_FILE,QUIT};
static char forced[] =  {MOVE_BLOCK,COPY_BLOCK,
                         DELETE_BLOCK,HIDE_BLOCK,
                         PARAGRAPH,BEGIN_BLOCK,
                         END_BLOCK,FIND,FIND_AGAIN};
static char options[] = {REFORM,INS};

static int (*ffuncs[])() =
    {load_file,save_file,merge_file,new_file,edit};
static int (*efuncs[])() =
    {edit,edit,edit,edit,edit,edit,edit,edit,edit};
static int (*ofuncs[])() =
    {edit,edit,edit};

MENU emn [] = {
    {"File",    NULL, fselcs, filehelp, fkeys,   ffuncs, 0},
    {"Edit",    NULL, eselcs, edithelp, forced,  efuncs, 0},
    {"Options", NULL, oselcs, opthelp,  options, ofuncs, 0},
    {NULL}
};

/* ------ filename data entry template and buffer ------- */
static char filename[65];
static char savefn [65];
static char filemask[65];

FIELD fn_template[] = {
    {2,14,1,filename,filemask,"filename"},
    {0}
};

/* ------- text find data entry template and buffer ------ */
static char findstr[71];
static char findmask[71];

FIELD find_template[] = {
    { 2,8,1,findstr,findmask,"findstr"},
    {0}
};

extern int forcechar;
extern struct edit_env ev;

static void editkeys(int c)
{
    switch(c)   {
        case REFORM:
            ev.reforming ^= TRUE;
            break;
        case NEW_FILE:
            new_file(1,1);
            break;
        case LOAD_FILE:
            load_file(1,1);
            break;
        case SAVE_FILE:
            save_file(1,1);
            break;
        case MERGE_FILE:
            merge_file(1,1);
            break;
        case FIND:
            if (!findstring())
                break;
        case FIND_AGAIN:
            ev.nowptr++;
            ev.nowptr = find(ev.nowptr, ev.lstptr-ev.nowptr);
            if (ev.nowptr != NULL)  {
                ev.curr_x = (ev.nowptr-ev.topptr) % ev.wwd;
                if (ev.nowptr >= ev.bfptr+ev.wwd*ev.wdo->ht)
                    ev.bfptr = ev.nowptr - ev.curr_x;
                ev.curr_y = (ev.nowptr - ev.bfptr) / ev.wwd;
            }
            else
                error_message("Not found ...");
            break;
        case ALT_F:
            editmenu(1);
            break;
        case ALT_E:
            editmenu(2);
            break;
        case ALT_O:
            editmenu(3);
            break;
        case EDITOR_MENU:
            editmenu(0);
            break;
        default:
            if (edit_extend)
                (*edit_extend)();
            else
                putch(BELL);
            break;
    }
}

static void editmenu(n)
{
    menu_select(emn, n);
}

static int edit(hs,vs)
{
    forcechar = emn[hs-1].mskeys[vs-1] & 255;
    return TRUE;
}

/* ---------- edit a file -------------- */
void fileedit(char *file)
{
    char *bf, *mb;
    extern void (*editfunc)();
    extern void (*status_line)();

    setmem(filename, 64, ' ');
    setmem(filemask, 64, '_');
    setmem(findmask, 70, '_');
    setmem(findstr, 70, ' ');
    establish_window(1,2,80,24,TEXTFG,TEXTBG,TRUE);
    editfunc = editkeys;
    status_line = statusline;
    mb = display_menubar(emn);
    statusline();
    if ((bf = malloc(BUFLEN)) != NULL)  {
        setmem(bf, BUFLEN, ' ');
        strcpy(filename, file);
        filename[strlen(filename)] = ' ';
        if (*file)
            read_file(" Loading ... ",bf,0,FALSE,FALSE);
        while (TRUE)    {
            text_editor(bf, MAXLINES, EDITWIDTH);
            if (bufferok("quit"))
                break;
        }
        free(bf);
    }
    restore_menubar(mb);
    delete_window();
}

/* ---------- load a file --------------- */
static int load_file(hs,vs)
{
    if (bufferok("reload"))
        strcpy(savefn, filename);
        if (get_filename(" Load what file? ", TRUE) != ESC) {
            setmem(ev.topptr, BUFLEN, ' ');
            read_file(" Loading ... ",ev.topptr,0,FALSE,TRUE);
            forcechar = BEGIN_BUFFER;
            ev.text_changed = FALSE;
        }
        else
            strcpy(filename, savefn);
    return TRUE;
}

/* ---------- merge a file into the edit buffer -------- */
static int merge_file(hs,vs)
{
    strcpy(savefn, filename);
    if (get_filename(" Merge what file? ", TRUE) != ESC)    {
        if (read_file(" Merging ... ",
                curr(0, ev.curr_y),
                lineno(ev.curr_y), TRUE, TRUE)) {
            forcechar = REPAINT;
            ev.text_changed = TRUE;
        }
    }
    strcpy(filename, savefn);
    return TRUE;
}

/* --------- save the file -------------- */
static int save_file(hs,vs)
{
    if (get_filename(" Save as what file? ", FALSE) != ESC)
        if (write_file())
            ev.text_changed = FALSE;
    return TRUE;
}

/* ---------- start a new file ------------- */
static int new_file(hs,vs)
{
    if (bufferok("erase"))
        if (get_filename(" Build as what file? ",TRUE)!=ESC){
            setmem(ev.topptr, BUFLEN, ' ');
            forcechar = BEGIN_BUFFER;
            ev.text_changed = FALSE;
        }
    return TRUE;
}

/* -------- read a file name ------------- */
static int get_filename(char *ttl, int clear)
{
    int rtn;

    establish_window(1,23,80,25,ENTRYFG,ENTRYBG,TRUE);
    window_title(ttl);
    gotoxy(3,2);
    cputs("File name:");
    rtn = data_entry(fn_template, clear, 1);
    delete_window();
    return rtn;
}

/* --------- write a file ------------ */
static int write_file()
{
    FILE *fp;
    int ln, i, ln1;
    char *cp, buf[EDITWIDTH+1];

    if ((fp = fopen(filename, "w")) == NULL)    {
        error_message(" Can't write that file! ");
        return FALSE;
    }
    notice(" Writing file ... ");
    /* ----- find the last significant line ----- */
    for (ln = MAXLINES-1; ln > -1; --ln)    {
        cp = ev.topptr + ln * EDITWIDTH;
        for (i = 0; i < EDITWIDTH; i++)
            if (*(cp + i) != ' ')
                break;
        if (i < EDITWIDTH)
            break;
    }
    for (ln1 = 0; ln1 <= ln; ln1++) {
        movmem(ev.topptr + ln1 * EDITWIDTH, buf, EDITWIDTH);
        i = EDITWIDTH-1;
        cp = buf;
        while (i >= 0 && *(cp + i) == ' ')
            --i;
        if (i == -1 || *(cp + i) != ' ')
            i++;
        *(cp + i) = '\n';
        *(cp + i + 1) = '\0';
        fputs(cp, fp);
    }
    fclose(fp);
    delete_window();
    return TRUE;
}

/* -------------- read (load or merge) a file ----------- */
static int
read_file(char *nt,char *ln,int lines,int merging,int needed)
{
    FILE *fp;
    char ibf[120];
    char *cp;
    int x;

    if ((fp = fopen(filename, "r")) != NULL)    {
        notice(nt);
        while (fgets(ibf, 120, fp) && lines < MAXLINES) {
            lines++;
            if (merging)    {
                movmem(ln,ln+EDITWIDTH,
                    BUFLEN-lines*EDITWIDTH);
                setmem(ln,EDITWIDTH,' ');
            }
            cp = ibf, x = 0;
            while (*cp && *cp != '\n')  {
                if (*cp == '\t')
                    x += TAB-(x%TAB);
                else
                    *(ln+x++) = *cp;
                cp++;
            }
            ln += EDITWIDTH;
        }
        fclose(fp);
        delete_window();
        return TRUE;
    }
    else if (needed)
        error_message("No such file can be found");
    return FALSE;
}

/* ----------- display a status line ----------- */
static void statusline()
{
    char stat[81], *st;
    int cl[81], *cp;
    static char msk[] =
"Line:%3d   Column:%2d   %-9.9s   %-21.21s   F1:Help    \
F10:Menu  ";
    unsigned y = 1;
    unsigned x = 1;
    unsigned attr = ((MENUFG | (MENUBG << 4)) << 8);

    if (ev.wwd) {
        y = (unsigned) (ev.nowptr-ev.topptr) / ev.wwd + 1;
        x = (unsigned) (ev.nowptr-ev.topptr) % ev.wwd + 1;
    }
    sprintf(stat,msk,y,x,
        (ev.edinsert ? "Insert" : "Overwrite"),
        (ev.reforming ? "Auto Paragraph Reform" : " "));
    for (st = stat, cp = cl; *st; st++)
        *cp++ = (*st & 255) | attr;
    __vram(__vptr(1,25),cl,80);
    set_help("editor");
}

/* -------- get a string to find --------- */
static int findstring()
{
    char *cp = findstr+60;
    int ans;

    establish_window(1,23,80,25,ENTRYFG,ENTRYBG,TRUE);
    gotoxy(2,2);
    cputs("Find?");
    ans = data_entry(find_template, TRUE, 1);
    delete_window();
    if (ans == ESC)
        return FALSE;
    while (*--cp == ' ')
        ;
    if (*cp)
        *(cp+1) = '\0';
    return TRUE;
}

/* -------- find a string in the buffer -------------- */
static char *find(char *bf, unsigned len)
{
    char *cp;

    for (cp = bf; cp < bf+len-strlen(findstr); cp++)
        if (strncmp(cp, findstr, strlen(findstr)) == 0)
            return cp;
    return NULL;
}

/* ---------- test for buffer changed ----------- */
static int bufferok(char *s)
{
    int c = 'Y';
    if (ev.text_changed)    {
        establish_window(23,11,56,13,ERRORFG,ERRORBG,TRUE);
        gotoxy(2,2);
        cprintf("Text has changed, %s? (y/n)", s);
        hidecursor();
        do
            putch(BELL), c = getkey();
        while (toupper(c) != 'Y' && toupper(c) != 'N');
        delete_window();
    }
    return toupper(c) == 'Y';
}

/* -------- small message ------------ */
static void notice(char *s)
{
    int lf = (80-strlen(s))/2-1;
    int rt = lf+strlen(s)+2;
    establish_window(lf,11,rt,13,HELPFG,HELPBG,TRUE);
    gotoxy(2,2);
    cputs(s);
}



[LISTING FIVE]


/* ----------- twrp.c ------------ */
#include <conio.h>
#include "window.h"
#include "editor.h"
#include "help.h"

void main(int, char **);
void fileedit(char *);

void main(int argc, char **argv)
{
    extern int inserting, MAXLINES;

    MAXLINES = 800;
    load_help("twrp.hlp");
    clear_screen();
    fileedit(argc > 1 ? argv[1] : "");
    clear_screen();
    inserting = FALSE;
    insert_line();
}



[LISTING SIX]


trwp (window.h, editor.h, help.h)
editshel (editor.h, menu.h, entry.h, help.h, window.h)
editor (editor.h, window.h)
entry (entry.h, window.h)
menu (menu.h, window.h)
help (help.h, window.h)
window (window.h)









Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.