_C PROGRAMMING COLUMN_
by Al Stevens

[LISTING 1]

/* ------------ help.h -------------- */

void load_help(char *);
void display_help(void);

extern char *help_window;
#define set_help(s) help_window=s


[LISTING 2]

/* --------- 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 3]

<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 4]

/* --------- 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 5]

/* ----------- 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 6]

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)



