                             LOCATING DATA 

                            Matthew Probert
                           Servile  Software



Locating a particular record quickly and easily is a major 
consideration of any data storage and retrieval system. It is all very 
well storing five hundred letters to prospective clients on a hard 
disc, but how do you retrieve a particular letter at a later date? 

DOS, the disk operating system usual to personnal computers, allows a 
file to be named with a name of up to eight characters in length, and 
with an optional further three character extension prefixed by a 
period. For example. "LETTER.1", "LETTER.2", "FRED.LET", 
"CUSTOMER.DOC" 

A nice solution may be to implement an integrated environment whereby 
document names can be displayed next to a description of the document. 
Then, the required document could be highlighted with a moving scroll 
bar, and the return key pressed to load the word processor along with 
the document. To implement this we need to make use of a DOS batch 
file. This batch file will call the document organiser program, which 
will in turn modify the batch file to then call the word processor. 


:start
echo off
docorg
IF ERRORLEVEL 1 GOTO END
wp 
GOTO start
:END
CLS

Listing 1. docbat.bat


The document organiser is listed in listing 2. Docorg.c. It will 
accept upto 1000 file names and descriptions. These are recorded in a 
text file, "docorg.dat". 

/* DOCORG.C */

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <dir.h>
#include <dos.h>
#include <io.h>
#include <alloc.h>
#include <signal.h>
#include <process.h>

#define    ON    0x06
#define OFF    0x20

/* Name of the program to be invoked on exit */
char *WP = "wp";

char *item[1000];
char *desc[1000];

int bg;
int bn;
int mc;
int bc;
int hi;
int start;
int row;
int max;
int wm;
int l;
int t;
int r;
int b;

union REGS inreg,outreg;

void BANNER(void);
void CURSOR(int);
void EDIT(void);
void FOOTER(void);
void GETOPTS(void);
void DISPLAY(void);
void HILITE(void);
void UNLITE(void);
void LOOP(void);
void MOVE(void);
void NEW(void);
void PROGS(void);
void DOWN(void);
void UP(void);

void CURSOR(int status)
{
    /* Display/hide cursor */

    inreg.h.ah = 0x01;
    inreg.h.ch = status;
    inreg.h.cl = 0x07;
    int86(0x10,&inreg,&outreg);
}

void DBOX(int l, int t, int r, int b)
{
    /* Draw a double line box */

    int n;
    char top[81];
    char bottom[81];

    strcpy(top,"");
    strcpy(bottom,"");

    strncat(top,"
                 ",r-l-1);
    strncat(bottom,"
                    ",r-l-1);

    strcat(top,"");
    strcat(bottom,"");

    window(1,1,80,25);
    gotoxy(l,t);
    cputs(top);

    for (n = t + 1; n < b; n++)
    {
        gotoxy(l,n);
        putch(186);
        gotoxy(r,n);
        putch(186);
    }
    gotoxy(l,b);
    cputs(bottom);
}
/* Draws a double line box around the described area */

void BOX(int l, int t, int r, int b)
{
    int n;
    char top[81];
    char bottom[81];

    strcpy(top,"");
    strcpy(bottom,"");

    strncat(top,"
                 ",r-l-1);
    strncat(bottom,"
                    ",r-l-1);

    strcat(top,"");
    strcat(bottom,"");

    window(1,1,80,25);
    gotoxy(l,t);
    cputs(top);
    for (n = t + 1; n < b; n++)
    {
        gotoxy(l,n);
        putch(179);
        gotoxy(r,n);
        putch(179);
    }
    gotoxy(l,b);
    cputs(bottom);
}

void AT(int row, int col)
{
    /* Set cursor position anywhere on the screen */

    inreg.h.bh = 0;
    inreg.h.dh = row;
    inreg.h.dl = col;
    inreg.h.ah = 0x02;
    int86 (0x10, &inreg, &outreg);
}

void WHERE(int *row, int *col)
{
    /* Read current cursor position */

    inreg.h.bh = 0;
    inreg.h.ah = 0x03;
    int86 (0x10, &inreg, &outreg);
    *row = outreg.h.dh;
    *col = outreg.h.dl;
}


void WRTCHA (unsigned char ch, unsigned char attrib,  int num)
{
    /* Write a character and attribute directly to the BIOS */

    inreg.h.al = ch;
    inreg.h.bh = 0;
    inreg.h.bl = attrib;
    inreg.x.cx = num;
    inreg.h.ah = 0x09;
    int86 (0x10, &inreg, &outreg);
}

void BANNER()
{
    /* Display top message box */

    /* Display an attractive backgorund */
    AT(0,0);
    WRTCHA(178,7,2000);

    window (2,2,78,4);
    textattr(30);
    clrscr();
    BOX(2,2,78,4);
    gotoxy(32,3);
    cprintf("Document Organiser");

    FOOTER();
}

void FOOTER()
{
    /* Display bottom message box */
    window (2,22,78,24);
    textattr(bn);
    clrscr();
    BOX(2,22,78,24);
    gotoxy(3,23);
    cprintf("[%c] Up   [%c] Down   [<] Select   [%c]  Edit   [Insert]  New   
             [Esc]  Quit",24,25,29);
}

void INPUT(char *text,unsigned length)
{
    /* Get a string from the operator */

    int key_pos;
    int key;
    int start_row;
    int start_col;
    int end;
    char temp[80];
    char *p;

    WHERE(&start_row,&start_col);

    key_pos = 0;
    end = strlen(text);
    for(;;)
    {
        key = getch();
        if (key == 0)
        {
            key = getch();
            if (key == 79)
            {
                while(key_pos < end)
                    key_pos++;
                AT(start_row,start_col + key_pos);
            }
            else
            if (key == 71)
                /* Home */
                key_pos = 0;
            else
            if ((key == 75) && (key_pos > 0))
                /* Left */
                key_pos--;
            else
            if ((key == 77) && (key_pos < end))
                /* Right */
                key_pos++;
            else
            if (key == 83)
            {
                p = text + key_pos;
                while(*(p+1))
                {
                    *p = *(p+1);
                    p++;
                }
                *p = 32;
                if (end > 0)
                    end--;
                AT(start_row,start_col);
                cprintf("%s ",text);
                if ((key_pos > 0) && (key_pos == end))
                {
                    key_pos--;
                }
            }
        }
        else
        if (key == 13)
            /* Return/Enter */
            break;
        else
        if ((key == 8) && (key_pos > 0))
        {
            end--;
            key_pos--;
            text[key_pos--] = '\0';
            strcpy(temp,text);
            p = text + key_pos + 2;
            strcat(temp,p);
            strcpy(text,temp);
            AT(start_row,start_col);
            cprintf("%-*.*s",length,length,text);
            key_pos++;
        }
        else
        if (key > 31 && key_pos < length)
        {
            if (key_pos <= end)
            {
                p = text + key_pos;
                memmove(p+1,p,end - key_pos);
                if (key_pos < length)
                    end++;
                text[end] = '\0';
            }
            text[key_pos++] = (char)key;
            if (key_pos > end)
            {
                end++;
                text[end] = '\0';
            }
            AT(start_row,start_col);
            cprintf("%-*.*s",length,length,text);
        }
        AT(start_row,start_col + key_pos);
    }
    text[end] = '\0';
}

void DISPLAY()
{
    /* Displays a window of DISPLAY options from the array 'options[]' */
    /* Starting at item 'start' */

    int n;

    wm = b - t - 1;

    if (wm > max)
        wm = max;

    window(l + 1,t + 1,r - 1,b);
    textattr(mc);
    clrscr();

    for(n = 0; n <= wm; n++)
    {
        gotoxy(2,n + 2);
        if (item[start + n] != NULL)
            cprintf("%-16.16s%s",item[start + n],desc[start + n]);
        else
            cprintf("%78.78c",32);
    }
}

void HILITE()
{
    /* Highlite the current row */

    if (max < 0)
        return;

    textattr(hi);
    gotoxy(2,row + 2);
    cprintf("%-16.16s",item[start + row]);
    textattr(mc);
}

void UNLITE()
{
    /* Unhighlite the current row */

    if (max < 0)
        return;

    textattr(mc);
    gotoxy(2,row + 2);
    cprintf("%-16.16s",item[start + row]);
}

void MOVE(void)
{
    /* A moving bar and scrolling window selection */

    int k;
    int left;
    int top;
    int right;
    int bottom;
    int n;

    BANNER();

    left = l-1;
    right = r+1;
    top = t;
    bottom = b+1;

    window (left,top,right,bottom);
    textattr(bg);
    clrscr();
    textattr(mc);
    DBOX(left+1,top,right-1,bottom);

    DISPLAY();

    k = 0;

    while (k != 13)
    {
        HILITE();

        if ((k = getch()) == 0)
        {
            k = getch();
            switch(k)
            {
                case 82:/* Insert */
                        if (max < 999)
                        {
                            max++;
                            row = wm;
                            start = max - wm;
                            if (row > max)
                                row = max;
                            if (start < 0)
                                start = 0;

                            DISPLAY();
                            NEW();
                            return;
                        }
                        break;

                case 72:UP();
                        break;

                case 80:DOWN();
                        break;

                case 73: /* Page Up */
                        for(n = 0; n < wm; n++)
                            UP();
                        break;

                case 81:/* Page Down */
                        for(n = 0; n < wm; n++)
                            DOWN();
                        break;


                case 71:/* Home */
                        row = 0;
                        start = 0;
                        DISPLAY();
                        break;

                case 79:/* End */
                        row = wm;
                        start = max - wm;
                        if (row > max)
                            row = max;
                        if (start < 0)
                            start = 0;
                        DISPLAY();
                        break;
            }
        }
        else
        if (k == 9)
        {
            /* Tab */
            EDIT();
        }
        else
        if (k == 27)
        {
            CURSOR(ON);
            exit(1);
        }
    }
}

void UP(void)
{
    /* Move highlight bar up one line */

    if (row > 0)
    {
        UNLITE();
        row--;
    }
    else
    if (start > 0)
    {
        start--;
        DISPLAY();
    }
}

void DOWN(void)
{
    /* Move highlight bar up one line */

    if ((row < wm) && (row < max - start))
    {
        UNLITE();
        row++;
    }
    else
    if (start < max - wm)
    {
        start++;
        DISPLAY();
    }
}

void GETOPTS()
{
    /* Read file descriptions file */

    FILE *fp;
    char *p;
    char text[80];

    if((fp = fopen("docorg.dat","r")) == NULL)
        return;

    do
    {
        p = fgets(text,15,fp);
        if (p)
        {
            text[strlen(text) - 1] = 0;
            item[max] = malloc(strlen(text)+1);
            strcpy(item[max],text);
            p = fgets(text,61,fp);
            text[strlen(text) - 1] = 0;
            desc[max] = malloc(strlen(text) + 1);
            strcpy(desc[max],text);
        }
    }
    while((p != NULL) && (max++ < 1000));

    fclose(fp);
    max--;
    return;
}

void NEW()
{
    /* Insert a new file name and details */
    /* Then exit through the batch file */

    FILE *fp;
    char name[14];
    char details[60];
    int n;
    int ok;

    gotoxy(2,row + 2);
    CURSOR(ON);
    strcpy(name,"");
    do
    {
        INPUT(name,14);
        ok = 1;
        for(n = 0; n < max; n++)
        {
            if (stricmp(name,item[n]) == 0)
            {
                window (2,21,78,23);
                textattr(bn);
                clrscr();
                BOX(2,21,78,23);
                gotoxy(26,22);
                cprintf("File Name Already Used. Please Reenter.");
                ok = 0;
            }
        }
    }
    while(!ok);

    if (*name == 0)
    {
        max--;
        return;
    }
    gotoxy(18,wherey());
    strcpy(details,"");
    INPUT(details,57);
    CURSOR(OFF);
    item[max] = malloc(strlen(name) + 1);
    strcpy(item[max],name);
    desc[max] = malloc(strlen(details) + 1);
    strcpy(desc[max],details);
    window (2,22,78,24);
    textattr(bn);
    clrscr();
    BOX(2,22,78,24);
    gotoxy(32,23);
    cprintf("Please Wait....");

    fp = fopen("docorg.dat","w+");
    for(n = 0; n <= max; n++)
        fprintf(fp,"%s\n%s\n",item[n],desc[n]);
    fclose(fp);
}

void EDIT()
{
    /* Amend details of selected file */

    FILE *fp;
    char details[60];
    int n;

    UNLITE();
    gotoxy(2,row + 2);
    CURSOR(ON);

    gotoxy(18,wherey());
    strcpy(details,desc[start + row]);
    INPUT(details,57);
    CURSOR(OFF);
    desc[start + row] = realloc(desc[start + row],strlen(details) + 1);
    strcpy(desc[start + row],details);
    window (2,22,78,24);
    textattr(bn);
    clrscr();
    BOX(2,22,78,24);
    gotoxy(32,23);
    cprintf("Please Wait....");
    
    fp = fopen("docorg.dat","w+");
    for(n = 0; n <= max; n++)
        fprintf(fp,"%s\n%s\n",item[n],desc[n]);
    fclose(fp);
    FOOTER();
    DISPLAY();
}


void LOOP()
{
    FILE *fp;
    int n;

    for(;;)
    {
        start = 0;
        row  = 0;
        max = 0;

        GETOPTS();

        r = 77;
        l = 3;

        t = 7;
        b = 18;

        CURSOR(OFF);
        FOOTER();
        MOVE();
        CURSOR(ON);
        for(n = 0; n < max; n++)
        {
            free(item[n]);
            free(desc[n]);
        }

        /* Amend the calling batch file */
        fp = fopen("docbat.bat","w+");
        fprintf(fp,":start\necho off\nwp\nIF ERRORLEVEL 1 GOTO END\n");
        fprintf(fp,"%s %s\nGOTO start\n:END\nCLS\n",WP,item[start + row]);
        exit(0);
    }
}


void main()
{
    /* Disable Ctrl-Break */
    signal (SIGINT, SIG_IGN);

    bg = 31;    /* Banner attribute */
    bn = 30;    /* Attribute for bottom message box */
    mc = 30;    /* Attribute for selection window */
    hi = 79;    /* Highlite colour */
    bc = 0;     /* Main screen background attribute */

    LOOP();
}

Listing 2. Docorg.c


To make use of docorg, run the batch file DOCBAT.BAT instead of your 
word processor. Then, if you wish to create a new document, press 
Insert. Docorg will then accept a file name, and a description for 
that file. After entering the description docorg will update the batch 
file and terminate, passing control back to the batch file. The batch 
file then continues from the next line calling the word processor with 
a parameter which is the name of the file entered in docorg. 

Files can be amended in a similar way. Simply highlight the name of 
the document to edit, and then press return. 

The only real limitation of docorg is that it does not accept path 
specifications for the files. This limits it to files in the current 
DOS directory. On the plus side, it can also be used with desk top 
publishing systems and spreadsheets. 


